Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating view in Cloudant (CouchDB) based on an object property value

I've been trying to find a solution for this requirement but I've hit many dead ends.

I'm using Cloudant as my data store of user documents. Each user document has a field (property) called 'items', which is an array of objects.

So a user document looks like this:

{
    "_id":"userid1",
    "_rev":"XX",
    "username": "foobaruser"
    "items":
    [
      {
          "date":1357879144069,
          "text":"don't cry because it's over, smile because it happened.",
          "cat":"determination"
      },
      {
          "date":1357879179209,
          "text":"those who mind don't matter, and those who matter don't mind.",
          "cat":"fitness"
      },
      {
          "date":1357883809736,
          "text":"be the change that you wish to see in the world.",
          "cat":"determination"
      },
      {
          "date":1357879179209,
          "text":"those who mind don't matter, and those who matter don't mind.",
          "cat":"hardwork"
      },
      {
          "date":1357879179209,
          "text":"those who mind don't matter, and those who matter don't mind.",
          "cat":"determination"
      }
   ]
}
  • There are multiple user documents like this in the data store and each document has the property "items"

Requirement:

  1. Ideally, i'd like to use a search function on a view and pass in a value for "cat", which then returns all the "items" in all docs that match the value of "cat".

    e.g. https://[username].cloudant.com/dbname/_design/views/_search/doc?q=determination

  2. The above search will return all the objects in "items" across all user docs which have "cat = determination" in a format similar to this:

    {
        "total_rows": 2,
        "rows": 
        [{
            "id": "userid1",
            "items": 
            [
                {
                    "date":1357879144069,
                    "text":"don't cry because it's over, smile because it happened.",
                    "cat":"determination"
                },
                {
                    "date":1357883809736,
                    "text":"be the change that you wish to see in the world.",
                    "cat":"determination"
                },
                {
                   "date":1357879179209,
                   "text":"those who mind don't matter, and those who matter don't mind.",
                   "cat":"determination"
                }
            ]
        },
        {
            "id": "userid2",
            "items": 
            [
                {
                    "date":135787966655,
                    "text":"Some text",
                    "cat":"determination"
                }
            ]
        }]
    }
    
  3. If this is not possible using "search", then is it possible to use secondary-indexes to achieve this?

like image 911
newbreedofgeek Avatar asked Mar 24 '23 03:03

newbreedofgeek


2 Answers

In CouchDB you can't pass in a dynamic value for a query parameter to a view (in your case cat=determination). The approach in CouchDB is to create a more general view, and then adjust how the result is sorted when you call the view to get at the data you need.

You'll need to create custom view in your db's design document to achieve this:

byUserItemCat: {
    map: function (doc) {
        if ( !doc.items || doc.items.length === 0 ) return;
        for ( var i=0; i<doc.items.length; i++ ) {
            emit(doc.items[i].cat,{doc._id,doc.items[i].date,doc.items[i].text});
        }
    }
}

So the above view takes each doc in the db, checks it has an items array with contents, and then loops over all the doc's items. For each item element it finds it emits cat into the result set as the index, this is important since we can then sort against against this index. We're free to build the result object in anyway we like (second argument to emit), and in the above case I'm building an object with the user id, and the item's date and text.

You'd call the view something like this, to get all the results:

curl -X GET http://127.0.0.1:5984/<db-name>/_design/<design-doc-name>/_view/byUserItemCat

And if you were just interested in the results where the index key (i.e. cat) was "determination" you'd do:

curl -X GET http://127.0.0.1:5984/<db-name>/_design/<design-doc-name>/_view/byUserItemCat?key="determination"
like image 160
Jed Richards Avatar answered Apr 27 '23 02:04

Jed Richards


Cloudant's search works at the granularity of returning a document. Your indexing function could index the categories in use across the items in the document and then your search would return the documents containing items with the category you mention. Your application code would then need to filter down to the items to return to the client.

Your index function would be something like (shamelessly cribbing from Wintamute):

if (!doc.items || doc.items.length === 0) return;
var item;
for (var i=0; i<doc.items.length; i++) {
  index("cat", doc.items[i].cat, {"index": "not_analyzed"});
}

The alternative is to break out the items into their own documents if you really want to be able to get just the items.

However, I'd second (and have up-voted) Wintamute's answer as I agree that the suggested view is the best solution in this specific case (find and display items by cat).

like image 32
Mike Rhodes Avatar answered Apr 27 '23 02:04

Mike Rhodes