tags:

views:

66

answers:

1

Hi,

I want to create pagination on application level using the CouchDB view API. The pagination uses cursors, so given a cursor, I will query the view for the n+1 documents starting with the given cursor as start key and output the n results as page and provide the n+1 result row as the cursor for the next page.

This works well as long as the view keys are also the keys for my view rows. Now this time all my docs have a date field and I emit them as map keys, because I want to sort via date. However, I can't use my cursors anymore like before.

I thought that is the reason the view API also provides startkey_docid for submitting such a cursor doc id, however this is obviously not true. It seems like this value is only applied if there are several equal rows per keys.

So, in short: I want a date-ordered view, but cursors based on the document ids. How can I do this?

Thanks in advance

Simplified view

function map(doc)
{
    emit(doc.date, {_id: doc._id});
}


Simplified view result:

{
"rows":[
    {"id":"123","key":"2010-06-26T01:28:13.555Z", value:{...}},
    {"id":"234","key":"2010-06-22T12:21:23.123Z", value:{...}},
    {"id":"987","key":"2010-06-16T13:48:43.321Z", value:{...}},
    {"id":"103","key":"2010-05-01T17:38:31.123Z", value:{...}},
    {"id":"645","key":"2009-07-21T21:21:13.345Z", value:{...}}
]
}

Application-level query with cursor 234, page size 3 should return:
    234, 987, 103

So how can I map this to a view?

+1  A: 

Why do you want cursors based on docid?

Map Reduce creates single dimensional indexes, so any non-key traversal will be expensive. However, I think you can do what you want without requiring traversing 2 indexes at the same time.

See for instance here how I paginate through a posts with a certain tag:

Sofa's CouchApp tag pagination

aka

http://jchris.couchone.com/sofa/_design/sofa/_list/index/tags?descending=true&reduce=false&limit=10&startkey=[%22life%22%2C{}]&endkey=[%22life%22]

The key in that view looks like ["tag","2008/10/25 04:49:10 +0000"] so you can paginate through by tag and, within tags, by time.

Edited

Ha! I just realized what you are trying to do. It is so very simple.

Forget all about docids, they should be random anyway and not related to anything so just forget docs even have ids for a second.

You say "Application-level query with cursor 234, page size 3 should return: 234, 987, 103"

Your cursor should not be 234. It should be the key "2010-06-22T12:21:23.123Z".

So in essence you use the key of the last row of results as the startkey for the next query. So eg startkey=""2010-06-22T12:21:23.123Z""&limit=3, then for each page you render, link to a query where the new startkey is the last returned key.

Bonus: with what I've just described, you will have the bottom row of page 2 be the top row of page 3. To fix this, add skip=1 to your query.

Bonus bonus: OK, what about when I have more than 3 docs that emitted to the same key in the view? Then the last key will always be the same as the first key, so you can't progress in pagination without expanding the limit parameter. Unless... you use startkey_docid (and set it do the id of the last row). That is the only time you should use startkey_docid.

J Chris A
Hmm, your tag use case is slighty different. But to take on the blog examples. Think of a blog entries that have date. You know what to paginate your entries ordered by date, without using the limit/skip pattern which should be avoided due to performance reasons.
PartlyCloudy
Oh and the reason why I want to use cursors instead of page numbers is because they are stateless. This is necessary for my REST use case.
PartlyCloudy
J Chris A
ahhh, gotcha. You can't do page numbers (except for skip...) in CouchDB anyway, you have to do linked list style pagination. Combined with the _changes feed it should be possible to to do a version that is not only stateless, but which displays new items which should be on the current page, in real time as they are created.
J Chris A
Ok, what I want is a linked list ordered by date of entries as view, but view segments should be delimitable by their doc._ids. But I have a sense of foreboding, that is not possible. Initially I thought so because of the `startkey_docid` and `endkey_docid` params.
PartlyCloudy
Cursors based upon document ids are needed just because the application specification demands it and I can't go around that. But I realized how to do it. The view index must be [date,id] and when I get a request for a page, I first have to retrieve the single document with the id, then take the date and use the date and the id for querying the view. This means one additional request, but I think I can't go around that.
PartlyCloudy