Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
497 views
in Technique[技术] by (71.8m points)

filter - CouchDB sorting and filtering in the same view

I'm trying to use CouchDB for a new app, and I need to create a view that sorts by multiple fields and also filters by multiple fields. Here is an example document, I've left out the _id and _rev to save myself some typing.

{
    "title": "My Document",
    "date": 1279816057,
    "ranking": 5,
    "category": "fun",
    "tags": [
        "couchdb",
        "technology"
    ],
}

From the documentation, I've learned that I can easily create a view that sorts by a field such as ranking.

function(doc) {
    emit(doc.ranking, doc);
}

I've also learned that I can easily filter by fields such as category

function(doc) {
    emit(doc.category, doc);
}

http://127.0.0.1:5984/database/_design/filter/_view/filter?key=%22fun%22

My problem is that I need to do a bunch of these things all at the same time. I want to filter based on category and also tag. I should be able to filter down to only documents with category of "fun" and tag of "couchdb". I want to sort those filtered results by ranking in descending order, then by date in ascending order, then by title in alphabetical order.

How can I create one view that does all of that sorting and filtering combined?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

For emitting more than one piece of data in a key, you'll want to read up on Complex Keys. You'll most likely end up emit()'ing a key that is an array made up of the category and tag. For example...

function(doc) {
  for(var i = 0; i < doc.tags.length; i++)
    emit([doc.category, doc.tags[i]], doc);
}

Now when you query ?key=["fun", "couchdb"] you'll get all the items in the fun category tagged with "couchdb". Or if you want all of the items in the fun category, regardless of their tag, then you can query with a range: ?startkey=["fun"]&endkey=["fun", {}]. Just remember, if your item has multiple tags, you'll get it multiple times in the results (because you emit()'d the doc once per tag).

To go the extra step of sorting by rating, date, and title you'll add two more elements to your array: an integer and either the ranking, date, or title. Remember, you can emit() more than once per map function. An example map function...

function(doc) {
  for(var i = 0; i < doc.tags.length; i++)
  {
     emit([doc.category, doc.tags[i], 0, doc.ranking], doc);
     emit([doc.category, doc.tags[i], 1, doc.title], doc);
     emit([doc.category, doc.tags[i], 2, doc.date], doc);
  }
}

Now your key structure is: ["category", "tag", 0 ... 2, rank/title/date]

You're basically grouping all of the rankings under 0, titles under 1, and dates under 2. Of course, you're transmitting a lot of data, so you could either break each of these groupings out into a separate view in your design document, or only return the doc's _id as the value (emit([ ...], doc._id);).

Get everything in the "fun" category with the "couchdb" tag (ascending):

?startkey=["fun", "couchdb"]&endkey=["fun", "couchdb", {}, {}]

Get everything in the "fun" category with the "couchdb" tag (descending):

?startkey=["fun", "couchdb", {}, {}]&endkey=["fun", "couchdb"]&descending=true

Get only rankings in the fun category with the couchdb tag (ascending):

?startkey=["fun", "couchdb", 0]&endkey=["fun", "couchdb", 0, {}]

Get only rankings in the "fun" category with the "couchdb" tag (descending):

?startkey=["fun", "couchdb", 0, {}]&endkey=["fun", "couchdb", 0]&descending=true

I hope this helps. Complex keys start to really show how powerful Map/Reduce is at slicing and dicing data.

Cheers.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...