如何在CouchDB中查询两种类型的记录

时间:2015-02-03 20:39:30

标签: asynchronous couchdb pouchdb nosql

我在从PouchDB数据库中获取两种依赖类型的数据时遇到问题。

我有一份我喜欢的汽车清单:

localDB.query(function(doc) {
  if (doc.type === ‘list’) {
    emit(doc);
  }
}, {include_docs : true}).then(function(response) {
  console.log(“cars”, response);

  // Save Cars List to app
  for(var i = 0; i < response.rows.length; i++) {
    addToCarsList(response.rows[i].id, response.rows[i].carNumber);
  }
  console.log(“Cars List: " + carsListToString());

  return response;

}).then(function(listRecord) {

  listRecord.rows.forEach(function(element, index){

    console.log(index + ' -> ', element);

    localDB.query(function(doc) {
      console.log("filtering with carNb = " + element.carNb);
      if (doc.type === 'defect' && doc.listId == getCurrentListId() && doc.carNb == element.carNb ) {
        emit(doc);
      }
    }, {include_docs : false}).then(function(result){
      console.log("defects", result);

    }).catch(function(err){
      console.log("an error has occurred", err);
    });
  });

}).catch(function(err) {
  console.log('error', err);
});

这是发生了什么。获得汽车列表后,对于每辆汽车,我想查询缺陷并存储在一些阵列中。然后,当完成所有查询时,我想构建保存数据的UI。

但是正在发生的事情是forEach被快速处理并且不等待内部异步的localDb.query。

如何根据父查询中的属性查询某些文档?我查看了PouchDB文档中的promises,但我无法理解如何做到这一点。

(请忘记卷曲引号和可能的lint错误,此代码是手工匿名化并且超简化)

2 个答案:

答案 0 :(得分:2)

您正在寻找的方法是Promise.all()(执行所有承诺并在完成后返回)。

但是,您的查询效率非常低。最好创建一个持久索引,否则它必须为每个query()(!)执行完整的数据库扫描。您可以阅读the PouchDB query guide了解详情。

我建议安装pouchdb-upsert plugin,然后执行:

// helper method
function createDesignDoc(name, mapFunction) {
  var ddoc = {
    _id: '_design/' + name,
    views: {}
  };
  ddoc.views[name] = { map: mapFunction.toString() };
  return ddoc;
}

localDB.putIfNotExists(createDesignDoc('my_index', function (doc) {
  emit([doc.type, doc.listId, doc.carNb]);
})).then(function () {
  // find all docs with type 'list'
  return localDB.query('my_index', {
    startkey: ['list'],
    endkey: ['list', {}],
    include_docs: true
  });
}).then(function (response) {
  console.log("cars", response);

  // Save Cars List to app
  for(var i = 0; i < response.rows.length; i++) {
    addToCarsList(response.rows[i].id, response.rows[i].carNumber);
  }
  console.log("Cars List: " + carsListToString());

  return response;
}).then(function (listRecord) {

  return PouchDB.utils.Promise.all(listRecord.rows.map(function (row) {
    // find all docs with the given type, listId, carNb
    return localDB.query('my_index', {
      key: ['defect', getCurrentListId(), row.doc.carNb],
      include_docs: true
    });
  }));
}).then(function (finalResults) {
  console.log(finalResults);
}).catch(function(err){
  console.log("an error has occurred", err);
});

我在这里使用了一些技巧:

  • 发出[doc.type, doc.listId, doc.carNb],它允许我们按类型或类型+ listId + carNb进行查询。
  • 在查询类型时,我们可以执行{startkey: ['list'], endkey: ['list', {}]},它只匹配类型&#34; list&#34;因为{}是&#34;更高&#34;比CouchDB对象整理顺序中的字符串。
  • PouchDB.utils.Promise是&#34;隐藏&#34; API,但如果你问我,它是非常安全的。我们不太可能改变它。

修改另一个选项是使用新的pouchdb-find插件,该插件提供简化的查询API,旨在取代现有的map / reduce query() API。

答案 1 :(得分:1)

另一种方法是同时将列表文档和缺陷文档同时删除,然后使用reduce like方法将它们合并在一起,将它们转换为对象数组:

{
  _id: 1,
  type: 'list',
  ...
  defects: [{
    type: 'defect'
    listId: 1
    ...
  }]
}

通过在一次调用中拉下列表和缺陷,您可以保存对pouchdb查询引擎的多次调用,但是您必须遍历每个结果以构建带有嵌入式缺陷数组的列表对象集合。

//这是未经测试的代码,因此可能无效,但您应该明白这一点     var _ = require('underscore');

// order documents results by list then defect
var view = function (doc) {
  if (doc.type === 'list') {
    emit([doc._id, doc.carNumber, 1);
  } else if (doc.type === 'defect') {
    emit([doc.listId, doc.carNb, 2])
  }
}

localDB.query(view, { include_docs: true })
  .then(function(response) {
    return _(response.rows)
      .reduce(function(m, r) {
        if (r.key[2] === 1) {
          // initialize 
          r.doc.defects = [];
          m.push(r.doc)
          return m;
        }
        if (r.key[2] === 2) {
          var list = _(m).last()
          if (list._id === r.key[0] && list.carNumber === r.key[1]) {
            list.defects.push(r.doc);
          }
          return m;
        }
      }, []);  
  })
  .then(function(lists) {
    // bind to UI
  });

使用沙发,我们发现减少对沙发引擎的调用会更高效,但我不知道这种方法对于PouchDB是否更好,但这应该可以作为一种解决方案,特别是如果您想要嵌入多个集合到一个列表文件。