Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PouchDB query using parameters

Tags:

pouchdb

Lets suppose we store cars (about 40MB) represented as JSON objects in a PouchDB, and we would like to search, based on the horsepower attribute. example in sql: select * from cars where HP > 100.

You can query pouchDB by key, but obviously HP is not the key of the document. Is there a way you can do this?

As far as I understand the map function,

function(doc) {
  if(doc.value) {
    emit(doc.value, null);
  }
}

it is not possible to access any variable within the outer scope of the function.

var horsePower = $scope.horsePowerInputField

function(doc) {
  if(doc.hp > horsePower) {
    emit(doc.value, null);
  }
}

so is there a possibility to query the database, parametrized based on non-key variables?

like image 685
Frank Baier Avatar asked Dec 22 '13 13:12

Frank Baier


3 Answers

As of PouchDB 2.0.0, closures in map/reduce queries are supported. Details here.

However, you should probably avoid them if you can, because

  1. They're not supported by CouchDB, only PouchDB
  2. Saved map/reduce views, which are much faster and will probably be added in 2.1.0, cannot support closures.

That being said, if you do want to use closures, you can now do this:

var horsePower = $scope.horsePowerInputField

function(doc, emit) { // extra 'emit' tells PouchDB to allow closures
  if(doc.hp > horsePower) {
    emit(doc.value, null);
  }
}
like image 70
nlawson Avatar answered Dec 22 '22 00:12

nlawson


Your map function loses its closure because it is re-evaluated inside of PouchDB (that's how it gets the emit function). This means you can't access any variables from your code, but you can still query the database.

In PouchDB, views are not persistent, so your query always looks at every document in the database, and you have to do the filtering after the map function. Something like this:

function findCars(horsePower, callback) {
  // emit car documents
  function map(doc) {
    if(doc.type == 'car' && doc.value) {
      emit(doc.value, null);
    }
  }

  // filter results
  function filter(err, response) {
    if (err) return callback(err);

    var matches = [];
    response.rows.forEach(function(car) {
      if (car.hp == horsePower) {
        matches.push(car);
      }
    });
    callback(null, matches);
  }

  // kick off the PouchDB query with the map & filter functions above
  db.query({map: map}, {reduce: false}, filter)
}

Is one way to solve this problem. Pouch will iterate over each document, passing it to your map function. When done, filter gets called with an array of all the emitted documents. filter does not lose its closure context, so you can filter the results based on horsepower or any other field here.

like image 21
jches Avatar answered Dec 22 '22 01:12

jches


It's better not to use closures. Do this instead:

var horsePower = $scope.horsePowerInputField;
db.query(function(doc) {emit(doc.hp)}, {startkey: horsePower, include_docs: true});
like image 36
mrded Avatar answered Dec 21 '22 23:12

mrded