Jud
Jud

Reputation: 552

Reducing CouchDB based on value from linked document

We currently have a CouchDB database with some documents that are created from other documents. This is indicated with a 'forked-from' field in the document.

For example:

{
"_id":"doc_1",
"_rev":"1-a23f9403a1d86648b2a304f5a4c78c31",
"doctype":"entry",
"data":"foo",
"permissions":"private"
}

{
"_id":"doc_2",
"_rev":"1-9c15e43a8c8d4ce1b00af94b328d268d",
"doctype":"entry",
"data":"bar",
"forked_from":"doc_1"
"permissions":"public"
}

{
"_id":"doc_3",
"_rev":"1-b13c761e43121c074c44736826f3ce7e",
"doctype":"entry",
"data":"bat",
"forked_from":"doc_2"
"permissions":"private"
}

We currently have a map/reduce view that will return the number of times a document has been forked, which tells us what the most popular one is.

function (doc) { 
    if (doc.doctype === 'entry' && doc.forked_from) {
        emit(doc.forked_from, 1);
    }
}

function(keys, values, rereduce) {
    return sum(values);
}

and the result of querying this view with group=true would be:

{"rows":[
{"key":"doc_1","value":1},
{"key":"doc_2","value":1}
]}

However, what we would like is to only return the documents that have permissions set to 'public', so that the queried result looks like:

{"rows":[
{"key":"doc_2","value":1}
]}

doc_1 should not be returned because it is private.

It doesn't seem that I can filter the results in the map stage, as I only have the doc id, not the entire document to check. I tried adding the document to the key using the linked documents feature and including {'_id': doc.forked_from} in the emit of the map, but CouchDB responds with 'include_docs is invalid for reduce views'.

Any suggestions on how I can filter the results of this view?

Upvotes: 2

Views: 1884

Answers (2)

bowcot84
bowcot84

Reputation: 36

You say you only have the _id, but you already check for the presence of doc.forked_from, so surely doc.permissions must be available?

So surely your map function would be...

function (doc) {
    if (doc.doctype === 'entry' && doc.forked_from && doc.permissions === 'public') {  
        emit(doc.forked_from, 1);  
    }
}  

Upvotes: 0

Mark
Mark

Reputation: 2135

I'm fairly new to CouchDB, but you should read:

CouchDB Joins

So I think what you need to do is to make use of complex keys and group_level. The way to do this would be to structure your map function as such:

function (doc) { 
    if (doc.doctype === 'entry' && doc.forked_from) {
        emit([doc.permissions, doc.forked_from], 1);
    }
}

The result would be something like this with group=true:

{"rows":[
{"key":["private", "doc_1"],"value":1},
{"key":["public", "doc_2"],"value":1},
{"key":["private", "doc_3"],"value":1},
]}

For you to only obtain the "public" documents, you would have to use a range query together with a group_level query parameter:

http://localhost/db/_design/entry/_view/getNumberForked?startkey=["public", 0]&endkey=["public", {}]&group_level=2

This would give you your result:

{"rows":[
{"key":["public", "doc_2"],"value":1}
]}

I hope this helps you solve your problem.

Upvotes: 2

Related Questions