问题描述
我只想获取具有至少与那些 groupIDs 之一匹配的 groups 的数据。我尝试了一些解决方案,但我仍然做错了。假设我们有这样的数据。
const groupIDs = ['124','245','678','999','111'];
const data = [
{
_id: 'example:123',_rev: 'example',name: 'John',groups: [
{ groupID: '124',type: 'ADMIN' },{ groupID: '345',type: 'PRACTITIONER' },{ groupID: '678',type: 'PATIENT' },]
},{
_id: 'example:456',_rev: 'example2',name: 'Yoda',type: 'PRACTITIONER' }
]
},];
这是我的尝试
db.createIndex({
index: { fields: ['groups.[].groupID'] }
})
.then((result) => {
db.find({
selector: { groups.groupID: { $in: groupIDs } },use_index: results.name
})
})
解决方法
这里肯定是使用Mango达到预期效果的许多方法之一。我还提供了一个Map / Reduce示例。
对于芒果来说,重点是创建索引和选择器。索引就像这样
db.createIndex({
index: {
fields: ['groups'],ddoc: 'my-index'
}
});
请注意,我更喜欢将索引命名为(ddoc)。
因此,我们需要能够处理数组字段的组合运算符,其中包括$in
和$elemMatch
运算符。
从CouchDB中找到文档 [1] :
$ elemMatch运算符匹配并返回包含以下内容的所有文档 一个数组字段,其中至少一个元素与提供的查询匹配 条件。
此外,$in
文档指出
文档字段必须存在于提供的列表中。
太好了!让我们使用$elemMatch
和$in
组合运算符在groups
数组中找到一组groupID。
selector: {
groups: {
$elemMatch: {
groupID: { $in: groupIDs }
}
}
以下代码段演示了使用上述选择器的Mango查询和使用keys
查询选项的Map / Reduce解决方案。
两者之间的重要性是,Map / Reduce为每个匹配项生成一个文档 ,这意味着可能存在多余的文档;如果芒果结果集匹配一次或多次
,它就会返回一个文档。
// convenience
const gel = id => document.getElementById(id);
const all_docs = 'allDocs';
const groups_groupID = 'groupID';
const groups_groupIDView = 'groupIDView';
const g_view_result = 'view_result';
const g_groupIdHints = 'groupIdHints';
const queryFns = {};
// show all the documents used in this example.
queryFns[all_docs] = () => db.allDocs({
include_docs: true
});
// Mango - find one or more groupIDs in the groups array.
// @groupIDs is an array of one or more values.
queryFns[groups_groupID] = (groupIDs /*array*/ ) => {
const query = {
selector: {
groups: {
$elemMatch: {
groupID: {
$in: groupIDs
}
}
}
},use_index: "my-index"
}
return db.find(query);
}
// Map/Reduce - query the groupIDView for one or more groupIDs
// @groupIDs is an array of one or more values.
queryFns[groups_groupIDView] = (groupIDs /*array*/ ) => db.query(
groups_groupIDView,{
include_docs: true,reduce: false,keys: groupIDs
});
// execute a query named in queryFns
const query = async(fnName,csv) => {
const html = [];
const view_result = gel(g_view_result);
try {
// convert csv to array
const groupIds = (csv || '').split(',').map(id => id.trim());
let docs = await queryFns[fnName](groupIds);
// Ensure docs is an array of documents (e.g. db.allDocs vs db.find result)
// Beware the SO 'Tidy' function smashes the null coalesce operator
docs = docs.rows ?? docs.docs;
html.push(`Matches: ${docs.length || 0}`);
// collect the docs for view
docs.forEach(doc =>
html.push(`<pre>${JSON.stringify(doc,undefined,3)}</pre>`)
);
} catch (e) {
// display any error message
html.unshift(e.message);
}
view_result.innerHTML = html.join('<hr/>');
}
// canned demo documents
function getDocsToInstall() {
return [{
_id: 'example:123',//_rev: 'example',name: 'John',groups: [{
groupID: '124',type: 'ADMIN'
},{
groupID: '345',type: 'PRACTITIONER'
},{
groupID: '678',type: 'PATIENT'
},]
},{
_id: 'example:456',// _rev: 'example2',name: 'Yoda',type: 'PRACTITIONER'
}
]
},]
}
//
// init db
//
let db;
(async() => {
db = new PouchDB('test',{
adapter: 'memory'
});
// install the docs into the db
const docs = getDocsToInstall();
await db.bulkDocs(docs);
// create a mango index for the 'groups' field.
db.createIndex({
index: {
fields: ['groups'],ddoc: "my-index"
}
});
// declare groups_groupIDView map/reduce index for groups.groupID
const ddoc = {
_id: '_design/' + groups_groupIDView,views: {
groupIDView: {
map: function(doc) {
if (doc.groups instanceof Array) {
doc.groups.forEach(g => {
emit(g.groupID);
})
}
}.toString()
}
}
};
// install the map/reduce design doc
await db.put(ddoc);
// candy - gather up and display groupIDs for this demo
const hints = {};
docs.forEach(doc => doc.groups.forEach(g => hints[g.groupID] = 1));
gel(g_groupIdHints).innerText = Object.keys(hints).join(',');
})();
.hide {
display: none
}
.label {
text-align: right;
margin-right: 1em;
}
.hints {
font-size: smaller;
}
<script src="https://cdn.jsdelivr.net/npm/pouchdb@7.1.1/dist/pouchdb.min.js"></script>
<script src="https://github.com/pouchdb/pouchdb/releases/download/7.1.1/pouchdb.memory.min.js"></script>
<script src="https://github.com/pouchdb/pouchdb/releases/download/7.1.1/pouchdb.find.min.js"></script>
<table>
<tr>
<td>
<button onclick='query(all_docs)'>Show All Docs</button>
</td>
</tr>
<tr>
<td>
<label for='groupID'>Find GroupIDs: </label>
<input type='text' id='groupID' />
<button onclick='query(groups_groupID,gel("groupID").value)'>Mango</button>
</td>
</tr>
<tr>
<td>
<label for='groupIDView'>Find GroupIDs: </label>
<input type='text' id='groupIDView' />
<button onclick='query(groups_groupIDView,gel("groupIDView").value)'>Map/Reduce</button>
</td>
</tr>
<tr>
<td>
<span class='hints'>Enter CSV of groupID's,e.g. <span id='groupIdHints'></span></span>
</td>
</tr>
<tr>
</table>
<div style='margin-top:2em'></div>
<div>
<pre id='view_result'></pre>
</div>