Reputation: 1
I have a CouchDB that contains items with something called save_data (actual data I need), rel (as related account - account linked to that document) and created_at (date of creation). When I first created my view which I called recent-items I thought that items in that view are sorted in order they were created in, but it didn't take long for me to discover just how wrong I was. I wanted to get all documents related to one user (rel, save_profile in my js which calls the db.view funcion) and then sort them based on created_at so I created map funcion:
function(doc) {
if (doc.rel) {
var p = doc.save_data || {};
var r = doc.rel || {};
var q = doc.created_at || {};
emit([doc.rel], {save_data: doc.save_data, rel: doc.rel});
}
};
and then I called it with these parameters:
db.view(design + "/recent-items", {
descending : "true",
limit : 10,
update_seq : true,
key : [save_profile],
success : function(data) {
...somecode...
}
});
and then I noticed that they didn't appear in the order I wanted, but sorted by their ID which makes sense now, but that's not what I needed. So I did this: I reworked map function so that it shows user and date in key (as fields to be sorted by)
function(doc) {
if (doc.rel) {
var p = doc.save_data || {};
var r = doc.rel || {};
var q = doc.created_at || {};
emit([doc.rel, doc.created_at], {save_data: doc.save_data, rel: doc.rel});
}
};
and then I used startkey instead of key in db.view like this:
db.view(design + "/recent-items", {
descending : "true",
limit : 10,
update_seq : true,
startkey : [save_profile, null],
success : function(data) {
...somecode...
}
});
so that I get all documents related to save_profile but sorted by date also. I did get them, but I also got documents from other users with it so that function is completely unreliable. What I did then is implement endkey also, like this:
db.view(design + "/recent-items", {
descending : "true",
limit : 10,
update_seq : true,
startkey : [save_profile, null],
endkey : [save_profile, {}],
success : function(data) {
...somecode...
}
});
but then I get empty view as result. Dates I use are in this format:
"2013-05-13T11:59:22.281Z"
and profiles are something like this:
{"rand":"0.26928815129213035","nickname":"Nickname","email":"Label for Gravatar","url":"URL","gravatar_url":"http://www.gravatar.com/avatar/35838c85593335493a1fa916863fee0c.jpg?s=40&d=identicon","name":"testacc"}
I also tried replacing [] and {} in start/endkeys with "0000-00-00T00:00:00.000Z" and "9999-99-99T99:99:99.999Z", but it changed nothing. So... Any ideas? As far as I've seen in other similar problems people just used {} as end key and left startkey without second parameter but that doesn't work here either.
Edit: Solved! Alright folks, believe it or not I've done it in this way: I changed my map function to display created at and profile in this way:
function(doc) {
if (doc.rel) {
var p = doc.save_data || {};
var r = doc.rel || {};
var q = doc.created_at || {};
emit([doc.rel, doc.created_at], {save_data: doc.save_data, rel: doc.rel});
}
};
and it appears that if you set descending to true before setting start and end keys you have to reverse the search interval so that endkey contains starting value and startkey containst ending value, like this:
db.view(design + "/recent-items", {
descending : "true",
update_seq : "true",
startkey : [save_profile.name, save_profile.rand, {}],
endkey : [save_profile.name, save_profile.rand, null],
inclusive_end : "true",
success : function(data) {
...somecode...
}
});
That did the trick and it works flawlessly (but also with a complete absence of logic).
Upvotes: 0
Views: 2672
Reputation: 368
I should have caught this before. So when you do the following, it works the way you'd like:
db.view('_design/test_views/_view/view1', startkey: ["account2"])
=> {"total_rows"db.view('_design/test_views/_view/view1')
=> {"total_rows"=>4, "offset"=>0, "rows"=>[{"id"=>"test1", "key"=>["account1", "2009-05-13T11:59:22.281Z"], "value"=>{"rel"=>"account1"}}, {"id"=>"test2", "key"=>["account2", "2012-05-13T11:59:22.281Z"], "value"=>{"rel"=>"account2"}}, {"id"=>"test3", "key"=>["account3", "2011-05-13T11:59:22.281Z"], "value"=>{"rel"=>"account3"}}, {"id"=>"test4", "key"=>["account4", "2010-05-13T11:59:22.281Z"], "value"=>{"rel"=>"account4"}}]}`
db.view('_design/test_views/_view/view1',startkey: ["account2"])
=> {"total_rows"=>4, "offset"=>1, "rows"=>[{"id"=>"test2", "key"=>["account2", "2012-05-13T11:59:22.281Z"], "value"=>{"rel"=>"account2"}}, {"id"=>"test3", "key"=>["account3", "2011-05-13T11:59:22.281Z"], "value"=>{"rel"=>"account3"}}, {"id"=>"test4", "key"=>["account4", "2010-05-13T11:59:22.281Z"], "value"=>{"rel"=>"account4"}}]}`
db.view('_design/test_views/_view/view1',
startkey: ["account2"],
endkey: ["account3",{}])
=> {"total_rows"=>4, "offset"=>1, "rows"=>[{"id"=>"test2", "key"=>["account2", "2012-05-13T11:59:22.281Z"], "value"=>{"rel"=>"account2"}}, {"id"=>"test3", "key"=>["account3", "2011-05-13T11:59:22.281Z"], "value"=>{"rel"=>"account3"}}]}`.
But when you set descending=true, you reverse the order first. Previously you were setting your start and end keys the way that you should when descending=false, but you need to reverse them when you change descending. The CouchDB Definitive Guide is great and talks about these reversed results, worth the read.
I totally missed that you were setting descending, sorry for the confusion.
Upvotes: 4
Reputation: 1
Alright folks, believe it or not I've done it in this way: I changed my map function to display created at and profile in this way:
function(doc) {
if (doc.rel) {
var p = doc.save_data || {};
var r = doc.rel || {};
var q = doc.created_at || {};
emit([doc.rel, doc.created_at], {save_data: doc.save_data, rel: doc.rel});
}
};
and it appears that if you set descending to true before setting start and end keys you have to reverse the search interval so that endkey contains starting value and startkey containst ending value, like this:
db.view(design + "/recent-items", {
descending : "true",
update_seq : "true",
startkey : [save_profile.name, save_profile.rand, {}],
endkey : [save_profile.name, save_profile.rand, null],
inclusive_end : "true",
success : function(data) {
...somecode...
}
});
That did the trick and it works flawlessly (but also with a complete absence of logic).
Upvotes: 0
Reputation: 368
I think the startkey [save_profile, null] is what is messing you up (although it works via curl). Whatever library you're using (db.view is some javascript lib or ruby CouchRest maybe?) may be recoding null as something else. You can always verify this compared to curl by breaking out the packet sniffer (i.e. Wireshark), although you'll get an idea how inefficient a library can be with all the extra requests it makes. Can make the troubleshooting more difficult.
What results do you get with curl? I mocked up your database, and here's what I get:
$ curl -X GET 'http://localhost:5984/test2/_design/test_views/_view/view1'
{"total_rows":4,"offset":0,"rows":[
{"id":"test1","key":["account1","2009-05-13T11:59:22.281Z"],"value":{"rel":"account1"}},
{"id":"test2","key":["account2","2012-05-13T11:59:22.281Z"],"value":{"rel":"account2"}},
{"id":"test3","key":["account3","2011-05-13T11:59:22.281Z"],"value":{"rel":"account3"}},
{"id":"test4","key":["account4","2010-05-13T11:59:22.281Z"],"value":{"rel":"account4"}}
]}
$ curl -X GET 'http://localhost:5984/test2/_design/test_views/_view/view1?startkey=\["account2"\]&endkey=\["account3",\{\}\]'
{"total_rows":4,"offset":1,"rows":[
{"id":"test2","key":["account2","2012-05-13T11:59:22.281Z"],"value":{"rel":"account2"}},
{"id":"test3","key":["account3","2011-05-13T11:59:22.281Z"],"value":{"rel":"account3"}}
]}
Notice that I am not using null, I'm just leaving that element of the key out. Also notice in curl (and possibly you're library), you have to be really careful what you put in start/end keys. I have to escape all bash stuff that even single quotes don't escape (i.e. [ { etc), and curl escapes spaces as %20, which is then used for the keys. A good approach is to run couchdb not forked out, or just watch its logs, and see what the incoming requests look like. Has been a source of issues for me.
You're using the wildcards in the keys, which is a cool feature. You may have seen this, I've re-read this snippet a few times to try to understand them.
I've beat my head on this similar problem too, until I really understood what views do. But what you're doing should be possible. Any more complicated searches, and I'd really consider Elasticsearch or something similar. The river install for it is really easy, and the queries are pretty similar. But you don't have to work around the limitations of views and orders.
Hope that helps.
Upvotes: 0