【发布时间】:2021-02-25 22:18:35
【问题描述】:
这是我在 sql 中需要的一个示例:
SELECT name FROM雇WHERE name LIKE %bro%
如何在 CouchDB 中创建这样的视图?
【问题讨论】:
标签: javascript couchdb
这是我在 sql 中需要的一个示例:
SELECT name FROM雇WHERE name LIKE %bro%
如何在 CouchDB 中创建这样的视图?
【问题讨论】:
标签: javascript couchdb
简单的答案是 CouchDB 视图不适合此。
更复杂的答案是,这种类型的查询在典型的 SQL 引擎中也往往效率很低,因此如果您同意与 any 解决方案进行权衡,那么 CouchDB 实际上具有优势让你选择你的权衡。
1。 SQL 方法
当您执行SELECT ... WHERE name LIKE %bro% 时,我熟悉的所有 SQL 引擎都必须执行所谓的“全表扫描”。这意味着服务器读取相关表中的每一行,然后暴力扫描字段以查看是否匹配。
您可以在 CouchDB 2.x 中通过使用 $regex operator 的 Mango 查询来执行此操作。对于基本情况,查询看起来像这样:
{"selector":{
"name": {
"$regex": "bro"
}
}}
似乎没有为区分大小写等提供任何选项,但您可以将其扩展为仅匹配开头/结尾或更复杂的模式。如果您还可以通过其他一些(可索引的)字段运算符来限制您的查询,那可能会提高性能。正如文档警告的那样:
正则表达式不适用于索引,因此它们不应用于过滤大型数据集。 […]
您也可以在 CouchDB 1.x 中进行全面扫描,使用 temporary view:
POST /some_database/_temp_view
{"map": "function (doc) { if (doc.name && doc.name.indexOf('bro') !== -1) emit(null); }"}
这将查看数据库中的每个文档,并为您提供匹配文档的列表。您可以调整 map 函数以匹配文档类型,或使用特定键发出排序 - emit(doc.timestamp) - 或一些对您的目的有用的数据值 - emit(null, doc.name)。
2。 “大量可用磁盘空间”方式
根据您的源数据大小,您可以创建一个索引,该索引发出每个可能的“内部字符串”作为其永久(磁盘上)视图键。也就是说,对于像“Dobros”这样的名字,你会emit("dobros"); emit("obros"); emit("bros"); emit("ros"); emit("os"); emit("s");。然后对于像 '%bro%' 这样的术语,您可以使用 startkey="bro"&endkey="bro\uFFFF" 查询您的视图以获取查找术语的所有出现。您的索引将大约是您的文本内容的大小 squared,但如果您需要比上面的完整数据库扫描更快地执行任意“在字符串中查找”并且有空间,这可能会起作用。不过,为substring searching 设计的数据结构会更好地为您服务。
这也给我们带来了...
3。 全文搜索方式
您可以使用 CouchDB 插件(couchdb-lucene 现在通过 Dreyfus/Clouseau 用于 2.x、ElasticSearch、SQLite's FTS)为您的文档生成一个面向文本的辅助索引。
请注意,大多数全文搜索索引don't naturally support 任意通配符前缀,可能是出于与我们在上面看到的空间效率类似的原因。通常全文搜索并不意味着“暴力二进制搜索”,而是“单词搜索”。不过,请查看您的全文引擎中可用的选项。
如果您真的不需要在字段中查找“bro”任何地方,您可以使用常规 CouchDB 视图实现基本的“查找以 X 开头的单词”搜索,只需在各种区域设置上进行拆分- 特定的单词分隔符并省略这些“单词”作为您的视图键。这将比上面更有效,与索引的数据量成比例。
【讨论】:
$regex 运算符,其性能与临时视图相似。我会更新我的答案。
不幸的是,使用 LIKE %...% 进行搜索并不是 CouchDB 视图的真正工作方式,但是您可以通过安装 couchdb-lucene 来完成大量搜索功能,它是一个全文搜索引擎,可以在您的数据库上创建索引,您可以进行更复杂的搜索。
在没有任何第三方工具的情况下“搜索”给定键的数据库的典型方法是创建一个视图,该视图发出您正在寻找的值作为键。在您的示例中:
function (doc) {
emit(doc.name, doc);
}
这会输出数据库中所有名称的列表。
现在,您将根据密钥的首字母“搜索”。例如,如果您要搜索以“bro”开头的名称。
/db/_design/test/_view/names?startkey="bro"&endkey="brp"
请注意,我采用了搜索参数的最后一个字母,并“递增”了其中的最后一个字母。同样,如果您想执行搜索,而不是汇总统计信息,您应该使用像 lucene 这样的全文搜索引擎。 (见上文)
【讨论】:
emit(doc.name, null) 并使用include_docs=true。
【讨论】:
^ 匹配字符串的开头,因此^sms 匹配以sms 开头的字符串,因此类似于sms%。 $ 匹配字符串的结尾,所以sms$ 匹配以sms 结尾的字符串,类似于%sms。
我为我的问题找到了一个简单的查看代码...
{
“getavailableproduct”:
{ "map": "function(doc) {
var prefix = doc['productid'].match(/[A-Za-z0-9]+/g);
if(prefix)
for(var pre in prefix) { emit(prefix[pre],null); }
}"
}
}
如果我将一个关键句子分成一个关键词,则从这个视图代码中...... 我可以打电话给
?key="[search_keyword]"
但我需要更复杂的代码,因为如果我运行此代码,我只能找到我输入的单词(例如:吃、食物等)...
但如果我想输入的不是一个完整的单词(例如:ea 来自 eat,或 foo 来自 food),则该代码不起作用..
【讨论】:
我知道这是一个老问题,但是:使用“列表”函数怎么样?您可以拥有所有普通视图,然后在设计文档中添加“列表”功能来处理视图的结果:
{
"_id": "_design/...",
"views": {
"employees": "..."
},
"lists": {
"by_name": "..."
}
}
附加到“by_name”函数的函数应该是这样的:
function (head, req) {
provides('json', function() {
var filtered = [];
while (row = getRow()) {
// We can retrive all row information from the view
var key = row.key;
var value = row.value;
var doc = req.query.include_docs ? row.doc : {};
if (value.name.indexOf(req.query.name) == 0) {
if (req.query.include_docs) {
filtered.push({ key: key, value: value, doc: doc});
} else {
filtered.push({ key: key, value: value});
}
}
}
return toJSON({ total_rows: filtered.length, rows: filtered });
});
}
当然,您也可以使用正则表达式。这不是一个完美的解决方案,但它对我有用。
【讨论】:
您可以像平常一样发出您的文档。 emit(doc.name, null); 我会在 name 上添加一个 toLowerCase() 以消除区分大小写。
然后使用一系列键查询视图,看看是否出现了“类似”查询的内容。
keys = differentVersions("bro"); // returns ["bro", "br", "bo", "ro", "cro", "dro", ..., "zro"]
$.couch("db").view("employeesByName", { keys: keys, success: dealWithIt } )
一些注意事项
显然,根据differentVersions 返回的内容,数组可以变得非常大非常快。您可能会在某个时候达到帖子数据限制,或者可以想象得到缓慢的查找。
结果与differentVersions 一样好,可以让您猜测该人要拼写的内容。显然,这个函数可以像你喜欢的那样简单或复杂。在这个例子中,我尝试了两种策略,a) 删除一个字母并推动它,b) 将位置 n 处的字母替换为所有其他字母。因此,如果有人一直在寻找“bro”,但输入了“gro”或“bri”甚至“bgro”,differentVersions 会在某个时候将其替换为“bro”。
虽然不理想,但仍然相当快,因为在 Couch 的 b 树中查找很快。
【讨论】:
为什么我们不能只在视图中使用 indexOf()?
【讨论】: