[英]MongoDB: Searching a text field using mathematical operators
我在 MongoDB 中有如下文档-
[
{
"_id": "17tegruebfjt73efdci342132",
"name": "Test User1",
"obj": "health=8,type=warrior",
},
{
"_id": "wefewfefh32j3h42kvci342132",
"name": "Test User2",
"obj": "health=6,type=magician",
}
.
.
]
我想运行一个查询,说health>6
,它应该返回"Test User1"
条目。 obj
键被索引为文本字段,因此我可以执行{$text:{$search:"health=8"}}
来获得完全匹配,但我正在尝试将数学运算符合并到搜索中。
我知道$gt
和$lt
运算符,但是,在这种情况下不能使用它,因为health
不是文档的关键。 最简单的方法是让health
成为文档的关键,但由于某些限制,我无法更改文档结构。
无论如何这可以实现吗? 我知道 mongo 支持运行 javascript 代码,不确定在这种情况下是否有帮助。
我认为在$text
搜索索引中是不可能的,但是您可以使用聚合查询将对象条件转换为对象数组,
$split
将obj
$split
为“,”,它将返回一个数组$map
迭代上述拆分结果数组的循环$split
通过“=”分割当前条件,它将返回一个数组$let
声明变量cond
来存放上述拆分结果$first
到从上述分割结果中返回的第一个元素k
为条件的关键$last
到最后一个元件从上述分割结果中返回v
为条件的值 "objTransform": [
{ "k": "health", "v": "9" },
{ "k": "type", "v": "warrior" }
]
$elemMatch
在同一对象中匹配键和值的$match
条件$unset
删除转换数组objTransform
,因为它不需要db.collection.aggregate([
{
$addFields: {
objTransform: {
$map: {
input: { $split: ["$obj", ","] },
in: {
$let: {
vars: {
cond: { $split: ["$$this", "="] }
},
in: {
k: { $first: "$$cond" },
v: { $last: "$$cond" }
}
}
}
}
}
}
},
{
$match: {
objTransform: {
$elemMatch: {
k: "health",
v: { $gt: "8" }
}
}
}
},
{ $unset: "objTransform" }
])
如果可以在客户端进行管理,则上述聚合查询的第二个升级版本将减少条件转换中的操作,
$split
将obj
$split
为“,”,它将返回一个数组$map
迭代上述拆分结果数组的循环$split
通过“=”分割当前条件,它将返回一个数组 "objTransform": [
["type", "warrior"],
["health", "9"]
]
$elemMatch
在数组元素中匹配键和值的$match
条件,“0”匹配数组的第一个位置,“1”匹配数组的第二个位置$unset
删除转换数组objTransform
,因为它不需要db.collection.aggregate([
{
$addFields: {
objTransform: {
$map: {
input: { $split: ["$obj", ","] },
in: { $split: ["$$this", "="] }
}
}
}
},
{
$match: {
objTransform: {
$elemMatch: {
"0": "health",
"1": { $gt: "8" }
}
}
}
},
{ $unset: "objTransform" }
])
使用 JavaScript 是做您想做的事情的一种方式。 下面是一个find
,它通过查找具有health=
文本后跟一个整数的文档来使用obj
上的索引(如果需要,您可以在正则表达式中用^
锚定它)。
然后,它使用 JavaScript 函数解析出实际整数,然后将您的方式子串过health=
部分,执行parseInt
以获取 int,然后是您在问题中提到的比较运算符/值。
db.collection.find({
// use the index on obj to potentially speed up the query
"obj":/health=\d+/,
// now apply a function to narrow down and do the math
$where: function() {
var i = this.obj.indexOf("health=") + 7;
var s = this.obj.substring(i);
var m = s.match(/\d+/);
if (m)
return parseInt(m[0]) > 6;
return false;
}
})
您当然可以根据自己的喜好调整它以使用其他运算符。
注意:我正在使用 MongoDB 可能不支持的 JavaScript 正则表达式功能。 我用蒙戈壳牌r4.2.6在那里支持。 如果是这种情况,在 JavaScript 中,您将不得不以不同的方式提取整数。
如果你想调整它,我提供了一个Mongo Playground来尝试它,但你会得到
Invalid query:
Line 3: Javascript regex are not supported. Use "$regex" instead
直到您更改它以解决上面提到的正则表达式问题。 尽管如此,如果您使用的是最新最好的,这不应该是一个限制。
表现
免责声明:此分析并不严谨。
我使用 MongoDB Compass 中的解释计划对一个小集合(较大的集合可能会导致不同的结果)运行了两个查询。 第一个查询是上面的那个; 第二个是相同的查询,但删除了obj
过滤器。
和
正如你所看到的,计划是不同的。 第一个查询检查的文档数较少,第一个查询使用索引。
执行时间没有意义,因为集合很小。 结果似乎与文档一致,但文档似乎与自身有点不一致。 这里摘录两段
使用
$where
运算符将包含 JavaScript 表达式的字符串或完整的 JavaScript 函数传递给查询系统。$where
提供了更大的灵活性,但要求数据库为集合中的每个文档处理 JavaScript 表达式或函数。
和
使用普通的非
$where
查询语句提供以下性能优势:
- MongoDB 将在
$where
语句之前评估查询的非$where
组件。 如果非$where
语句不匹配任何文档,MongoDB 将不会使用$where
执行任何查询评估。- 非
$where
查询语句可能使用索引。
我不完全确定该怎么做,TBH。 作为通用解决方案,它可能很有用,因为您似乎可以生成可以处理所有运算符的查询。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.