很遗憾,您不会使用$regex 之类的方式获得快速搜索,因为[1]
$regex、$ne 和 $not 不能使用磁盘索引,必须使用
而是在内存中过滤。
就是这样 - 你的情况非常低效。
在你的帖子下的评论中你说
...但是可以使用任何其他搜索单词开头的东西。我会做
如果需要,所有文档都小写。观看以匹配“搜索%”
太棒了!这很容易。但是,如果您绝对必须使用 Mango 查询,这将无法实现 - 满足您的读取要求的最佳选择是使用 map/reduce。
为简单起见,下面的 sn-p 创建了一个 map/reduce 视图,索引两个字段中的任何单词,name 和 category。这是map 函数。
function(doc) {
const regex = /[\s\.;]+/gi;
['name', 'category'].forEach(field => {
if (doc[field]) {
const words = doc[field].replaceAll(regex,',').split(',');
words.forEach(word => {
word = word.trim();
if (word.length) {
emit(word.toLocaleLowerCase(), [field, word]);
}
});
}
});
}
地图功能的亮点是
- 多个文档字段来源
- 所有索引词都是小写的
- 在索引行value中,保留字段和原词
请注意最后一点,例如您可以进行后期处理以过滤掉某些字段,显示原始单词等。
现在可以查询前缀搜索了。
async function search(term, descending) {
// snip snip
term = term.toLocaleLowerCase();
const result = await db.query(view_index, {
include_docs: false,
reduce: false,
descending: descending,
startkey: descending ? term + '\uFFF0' : term,
endkey: descending ? term : term + '\uFFF0'
});
// snip snip
}
注意descending 属性;需要颠倒startkey 和endkey。
运行下面的 sn-p,它使用 Lorem Ipsum 生成器中的单词索引 10k 个文档。它提供了相对较快的响应 - 但请注意此演示使用内存适配器,因此您的里程可能会有所不同,但是我相信通过针对您的特定用例进行一些调整,您应该获得不错的响应时间。祝你好运!
p.s.我建议运行 sn-p 全屏。
const LoremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Amet consectetur adipiscing elit duis tristique. Sed risus ultricies tristique nulla aliquet enim. Pharetra pharetra massa massa ultricies mi. Nisl nisi scelerisque eu ultrices vitae auctor eu. Tellus rutrum tellus pellentesque eu. Justo donec enim diam vulputate ut pharetra sit amet aliquam. Non pulvinar neque laoreet suspendisse interdum consectetur libero. Pellentesque habitant morbi tristique senectus et netus et malesuada. Nisl purus in mollis nunc sed. Ultricies integer quis auctor elit. Ultrices in iaculis nunc sed augue lacus viverra.Eu scelerisque felis imperdiet proin fermentum. Ultrices dui sapien eget mi proin sed libero. Bibendum neque egestas congue quisque egestas diam in arcu cursus. Pretium lectus quam id leo in vitae turpis massa sed. Sed vulputate mi sit amet mauris commodo quis. Elementum eu facilisis sed odio morbi quis commodo odio. Sed viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Id interdum velit laoreet id donec ultrices. Aenean et tortor at risus viverra adipiscing at. Gravida rutrum quisque non tellus orci ac auctor. Lobortis mattis aliquam faucibus purus in massa tempor nec feugiat. Tempus imperdiet nulla malesuada pellentesque elit eget gravida cum. Justo donec enim diam vulputate. Pharetra vel turpis nunc eget lorem dolor sed viverra. Neque gravida in fermentum et. Vel eros donec ac odio. Egestas egestas fringilla phasellus faucibus.Aliquam eleifend mi in nulla posuere sollicitudin aliquam ultrices sagittis. Sit amet cursus sit amet dictum sit amet justo donec.";
const view_index = "view_index";
const max_docs = 10000;
async function search(term, descending) {
const resultView = gel('results');
resultView.classList.add('hide');
term = term.trim();
if (term.length) {
let start = Date.now();
term = term.toLocaleLowerCase();
const result = await db.query(view_index, {
include_docs: false,
reduce: false,
descending: descending,
startkey: descending ? term + '\uFFF0' : term,
endkey: descending ? term : term + '\uFFF0'
});
const mark = Date.now() - start;
const matches = `<div>${uniquify(result.rows).join("<br/>")}</div>`;
const metrics = `<div>Matches: ${result.rows.length}/${mark}ms</div>`;
resultView.innerHTML = `<div>${metrics}${matches}</div>`
resultView.classList.remove('hide');
}
}
// gather up unique words that matched. This is sloppy!
function uniquify(rows) {
let arr = [];
rows.forEach(row => {
// note we're displaying the case-sensitive word!
if (arr.indexOf(row.value[1]) === -1) arr.push(row.value[1]);
});
return arr;
}
//
// pouchdb boilerplate code
//
const gel = id => document.getElementById(id);
let db;
// init example db instance
async function initDb() {
const start = Date.now();
db = new PouchDB('test', {
adapter: 'memory'
});
// declare view_index
const ddoc = {
_id: '_design/' + view_index,
views: {}
};
ddoc.views[view_index] = {
map: function(doc) {
const regex = /[\s\.;]+/gi;
['name', 'category'].forEach(field => {
if (doc[field]) {
const words = doc[field].replaceAll(regex,
',').split(',');
words.forEach(word => {
word = word.trim();
if (word.length) {
emit(word.toLocaleLowerCase(), [field, word]);
}
});
}
});
}.toString()
};
// install the map/reduce design doc
await db.put(ddoc);
// insert the docs.
console.log(`adding ${max_docs} documents...`);
await db.bulkDocs(getDocsToInstall());
// force the index to build
console.log(`Waiting for index '${view_index}' to build...`);
await db.query(view_index, {
reduce: true
});
console.log(`db inited in ${Date.now()-start}ms`);
}
// canned test documents
function getDocsToInstall() {
const docs = new Array(max_docs);
const words = LoremIpsum.split(' ');
const getWord = () => {
return words[Math.floor(Math.random() * Math.floor(words.length))];
}
for (let i = 0; i < docs.length; i++) {
docs[i] = {
name: getWord(),
category: getWord()
};
}
return docs;
}
initDb().then(() => {
// setup input listener
const searchFn = () => {
search(gel('term').value, gel('sort').checked);
};
gel('term').addEventListener('input', searchFn);
gel('sort').addEventListener('input', searchFn);
gel('view').classList.remove('hide')
});
.hide {
display: none
}
.label {
text-align: right;
margin-right: 1em;
}
.hints {
font-size: smaller;
}
<script src="//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>
<table id='view' class='hide'>
<tr>
<td class='label'>
<label for='term'>Term</label>
</td>
<td>
<input id='term' type='text' />
</td>
</tr>
<tr>
<td> </td>
<td class='hints'><em>hint:</em> Lorem, ipsum, dolor, sit, amet, consectetur, adipiscing, elit
</td>
</tr>
<tr>
<td class='label'>
<label for='sort'>Sort Desc</label>
</td>
<td>
<input id='sort' type='checkbox' />
</td>
</tr>
</table>
<div style='margin-top:2em'></div>
<hr/>
<div id='results' class='hide'>
</div>
1PouchDB Guide - Further reading