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

How to query two types of records in CouchDB

我在从 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 文档中的承诺,但我不明白该怎么做。

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

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

但是,您的查询已经很低效了。最好创建一个持久索引,否则它必须对每个 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);
});

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

  • emit [doc.type, doc.listId, doc.carNb],允许我们按类型查询或者按type+listId+carNb查询。
  • 当只查询类型时,我们可以做 {startkey: ['list'], endkey: ['list', {}]},它只匹配类型为 "list" 的那些,因为 {} 比 CouchDB 中的字符串 "higher"对象整理顺序。
  • PouchDB.utils.Promise 是一个 "hidden" API,但如果你问我的话,使用它是非常安全的。我们不太可能改变它。

编辑 另一种选择是使用新的 pouchdb-find 插件,它提供了一个简化的查询 API,旨在取代现有的 map/reduce query() API.

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

{
  _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
  });

对于 couch,我们发现减少对 couch 引擎的调用可以提高性能,但我不知道这种方法是否更适合 PouchDB,但这应该是一种解决方案,特别是如果你想嵌入几个集合到一个列表文档中。