如何使用类似 SQL 的运算符查询 PouchDB
How to query PouchDB with SQL-like operators
作为 PouchDB/CouchDB 的新手,我仍在思考如何在不同情况下正确使用 map/reduce。
假设我有这样的文档结构:
{
_id: 'record/1',
labels: {
// many-to-many relationship
'label/1': true, // let's assume that this is 'Label A'
'label/3': true, // 'Label C'
'label/4': true // 'Label D'
}
},
{
_id: 'record/2',
labels: {
'label/1': true, // 'Label A'
'label/2': true // 'Label B'
}
}
为要搜索的 db.query
函数定义视图的正确方法是什么:
- 记录 'Label A' 或 'Label B'
- 记录 'Label A' 和 'Label B'
PouchDB/CouchDB mapreduce 查询中没有 OR
操作,因此您必须将其分成两个单独的查询。
最终这些类型的操作将在 pouchdb-find 中得到支持,但在撰写本文时,$or
尚未实现。
尽管我很想使用 pouchdb-find
插件,但我找不到实现所需功能的方法。相反,我使用了一个解决方法:
更改文档结构以将标签 ID 存储在数组中
{_id: 'record/1', name: 'Record 1', labels: ['label/1', 'label/2', 'label/3']},
// may not be sorted when being stored
{_id: 'record/2', name: 'Record 2', labels: ['label/1', 'label/5', 'label/7', 'label/3']},
{_id: 'record/3', name: 'Record 3', labels: ['label/2', 'label/3', 'label/4', 'label/5']}
创建设计文档
它将为每条记录发出多个复杂键,以升序表示所有可能的标签映射。 map
函数将利用递归过程生成密钥:
{
_id: '_design/records-with-labels',
views: {
'records-with-labels': {
map: function(doc) {
// important: sort them so that the required keys to be created are lesser
var labelIds = doc.labels.sort();
var lastIx = labelIds.length - 1;
var emitKey = function emitKey(currentKey, currentIx) {
console.log('emitting: ' + currentKey.join(',') + ' for ' + doc._id);
emit(currentKey, null);
var nextIx = currentIx + 1;
for (var jumpIx = nextIx + 1; jumpIx <= lastIx; jumpIx++) {
var jumpedLabelId = labelIds[jumpIx];
var jumpingKey = currentKey.concat([jumpedLabelId]);
console.log('emitting: ' + jumpingKey.join(',') + ' for ' + doc._id);
emit(jumpingKey, null);
}
if (nextIx > lastIx) {
return;
}
var nextLabelId = labelIds[nextIx];
currentKey.push(nextLabelId);
emitKey(currentKey, currentIx + 1);
};
labelIds.forEach(function(labelId, i) {
emitKey([labelId], i);
});
}.toString()
}
}
}
例如,文档 record/1
将生成这些键:
emitting: label/1 for record/1
emitting: label/1,label/3 for record/1
emitting: label/1,label/2 for record/1
emitting: label/1,label/2,label/3 for record/1
emitting: label/2 for record/1
emitting: label/2,label/3 for record/1
emitting: label/3 for record/1
正在查询
我只需要确保查询标签按升序排序即可。
查询具有'label/1'和'label/3'的记录:
Db.query('records-with-labels', {
key: ['label/1', 'label/3']
});
查询具有'label/3'或'label/3'的记录:
Db.query('records-with-labels', {
keys: [['label/1'], ['label/3']]
});
这将为我们提供具有两个标签的重复记录,但 reduce 函数应该有助于消除它们。
结论
现在我不知道是否有更好的解决方案,但这对我来说已经足够了,因为在我的情况下,一条记录不会有太多标签。
如果您有更好的建议,欢迎评论或修改答案。
这是一个较旧的post。但是,可以使用 underscore.js 来帮助解决某些查询。它可以帮助提取您想要的数据,而无需多次访问数据库(除非您愿意)。
作为 PouchDB/CouchDB 的新手,我仍在思考如何在不同情况下正确使用 map/reduce。
假设我有这样的文档结构:
{
_id: 'record/1',
labels: {
// many-to-many relationship
'label/1': true, // let's assume that this is 'Label A'
'label/3': true, // 'Label C'
'label/4': true // 'Label D'
}
},
{
_id: 'record/2',
labels: {
'label/1': true, // 'Label A'
'label/2': true // 'Label B'
}
}
为要搜索的 db.query
函数定义视图的正确方法是什么:
- 记录 'Label A' 或 'Label B'
- 记录 'Label A' 和 'Label B'
PouchDB/CouchDB mapreduce 查询中没有 OR
操作,因此您必须将其分成两个单独的查询。
最终这些类型的操作将在 pouchdb-find 中得到支持,但在撰写本文时,$or
尚未实现。
尽管我很想使用 pouchdb-find
插件,但我找不到实现所需功能的方法。相反,我使用了一个解决方法:
更改文档结构以将标签 ID 存储在数组中
{_id: 'record/1', name: 'Record 1', labels: ['label/1', 'label/2', 'label/3']},
// may not be sorted when being stored
{_id: 'record/2', name: 'Record 2', labels: ['label/1', 'label/5', 'label/7', 'label/3']},
{_id: 'record/3', name: 'Record 3', labels: ['label/2', 'label/3', 'label/4', 'label/5']}
创建设计文档
它将为每条记录发出多个复杂键,以升序表示所有可能的标签映射。 map
函数将利用递归过程生成密钥:
{
_id: '_design/records-with-labels',
views: {
'records-with-labels': {
map: function(doc) {
// important: sort them so that the required keys to be created are lesser
var labelIds = doc.labels.sort();
var lastIx = labelIds.length - 1;
var emitKey = function emitKey(currentKey, currentIx) {
console.log('emitting: ' + currentKey.join(',') + ' for ' + doc._id);
emit(currentKey, null);
var nextIx = currentIx + 1;
for (var jumpIx = nextIx + 1; jumpIx <= lastIx; jumpIx++) {
var jumpedLabelId = labelIds[jumpIx];
var jumpingKey = currentKey.concat([jumpedLabelId]);
console.log('emitting: ' + jumpingKey.join(',') + ' for ' + doc._id);
emit(jumpingKey, null);
}
if (nextIx > lastIx) {
return;
}
var nextLabelId = labelIds[nextIx];
currentKey.push(nextLabelId);
emitKey(currentKey, currentIx + 1);
};
labelIds.forEach(function(labelId, i) {
emitKey([labelId], i);
});
}.toString()
}
}
}
例如,文档 record/1
将生成这些键:
emitting: label/1 for record/1
emitting: label/1,label/3 for record/1
emitting: label/1,label/2 for record/1
emitting: label/1,label/2,label/3 for record/1
emitting: label/2 for record/1
emitting: label/2,label/3 for record/1
emitting: label/3 for record/1
正在查询
我只需要确保查询标签按升序排序即可。
查询具有'label/1'和'label/3'的记录:
Db.query('records-with-labels', {
key: ['label/1', 'label/3']
});
查询具有'label/3'或'label/3'的记录:
Db.query('records-with-labels', {
keys: [['label/1'], ['label/3']]
});
这将为我们提供具有两个标签的重复记录,但 reduce 函数应该有助于消除它们。
结论
现在我不知道是否有更好的解决方案,但这对我来说已经足够了,因为在我的情况下,一条记录不会有太多标签。
如果您有更好的建议,欢迎评论或修改答案。
这是一个较旧的post。但是,可以使用 underscore.js 来帮助解决某些查询。它可以帮助提取您想要的数据,而无需多次访问数据库(除非您愿意)。