Hypnic Jerk
Hypnic Jerk

Reputation: 1192

CouchDB query issues

I will start off by saying while I am not new to CouchDB, I am new to querying the views using JavaScript and the web.

I have looked at multiple other questions on here, including CouchDB - Queries with params, couchDB queries, Couchdb query with AND operator, CouchDB Querying Dates, and Basic CouchDB Queries, just to list a few.

While all have good information in them, I haven't found one that has my particular problem in it.

I have a view set up like so:

function (docu) {
  if(docu.status && docu.doc && docu.orgId.toString() && !docu.deleted){
    switch(docu.status){
      case "BASE":
        emit(docu.name, docu);
      break;
      case "AIR":
        emit(docu.eta, docu);
      break;
      case "CHECK":
        emit(docu.checkTime, docu);
      break;
    }
  }
}

with all documents having a status, doc, orgId, deleted, name, eta, and checkTime. (I changed doc to docu because of my custom doc key.

I am trying to query and emit based on a set of keys, status, doc, orgId, where orgId is an integer.

My jQuery to do this looks like so:

$.couch.db("myDB").view("designDoc/viewName", {
    keys : ["status","doc",orgId],
    success: function(data) {
        console.log(data);
    },
    error: function(status) {
        console.log(status);
    }
});

I receive

{"total_rows":59,"offset":59,"rows":[

]}

Sometimes the offset is 0, sometimes it is 59. I feel I must be doing something wrong for this not to be working correctly.

So for my questions:

  1. I did not mention this, but I had to set docu.orgId.toString() because I guess it parses the URL as a string, is there a way to use this number as a numeric value?
  2. How do I correctly view multiple documents based on multiple keys, i.e. if(key1 && key2) emit(doc.name, doc)
  3. Am I doing something obviously wrong that I lack the knowledge to notice?

Thank you all.

Upvotes: 1

Views: 670

Answers (1)

fet
fet

Reputation: 624

You're so very close. To answer your questions

  1. When you're using docu.orgId.toString() in that if-statement you're basically saying: this value must be truthy. If you didn't convert to string, any number, other than 0, would be true. Since you are converting to a string, any value other than an empty string will be true. Also, since you do not use orgId as the first argument in an emit call, at least not in the example above, you cannot query by it at all.
  2. I'll get to this.
  3. A little.

The thing to remember is emit creates a key-value table (that's really all a view is) that you can use to query. Let's say we have the following documents

{type:'student',   dept:'psych',     name:'josh'},
{type:'student',   dept:'compsci',   name:'anish'},
{type:'professor', dept:'compsci',   name:'kender'},
{type:'professor', dept:'psych',     name:'josh'},
{type:'mascot',                      name:'owly'}

Now let's say we know that for this one view, we want to query 1) everything but mascots, 2) we want to query by type, dept, and name, all of the available fields in this example. We would write a map function like this:

function(doc) {

  if (doc.type === 'mascot') { return; } // don't do anything

  // allow for queries by type
  emit(doc.type, null); // the use of null is explained below

  // allow queries by dept
  emit(doc.dept, null);

  // allow for queries by name
  emit(doc.name, null);

}

Then, we would query like this:

// look for all joshs
$.couch.db("myDB").view("designDoc/viewName", {
  keys : ["josh"],
  // ...
});

// look for everyone in the psych department
$.couch.db("myDB").view("designDoc/viewName", {
  keys : ["psych"],
  // ...
});

// look for everyone that's a professor and everyone named josh
$.couch.db("myDB").view("designDoc/viewName", {
  keys : ["professor", "josh"],
  // ...
});

Notice the last query isn't and in the sense of a logical conjunction, it's in the sense of a union. If you wanted to restrict what was returned to documents that were only professors and also joshs, there are a few options. The most basic would be to concatenate the key when you emit. Like

emit('type-' + doc.type + '_name-' + doc.name, null);

You would then query like this: key : ["type-professor_name-josh"]

It doesn't feel very proper to rely on strings like this, at least it didn't to me when I first started doing it, but it is a quite common method for querying key-value stores. The characters - and _ have no special meaning in this example, I simply use them as delimiters.

Another option would be what you mentioned in your comment, to emit an array like

emit([ doc.type, doc.name ], null);

Then you would query like

key: ["professor", "josh"]

This is perfectly fine, but generally, the use case for emitting arrays as keys, is for aggregating returned rows. For example, you could emit([year, month, day]) and if you had a simple reduce function that basically passed the records through:

function(keys, values, rereduce) {
  if (rereduce) {
    return [].concat.apply([], values);
  } else {
    return values;
  }
}

You could query with the url parameter group_level set to 1 or 2 and start querying by year and month or just year on the exact same view using arrays as keys. Compared to SQL or Mongo it's mad complicated and convoluted, but hey, it's there.

The use of null in the view is really for resource saving. When you query a view, the rows contain an _id that you can use in a second ajax call to get all the documents from, for example, _all_docs.

I hope that makes sense. If you need any clarification you can use the comments and I'll try my best.

Upvotes: 2

Related Questions