如何匹配 MongoDB 中嵌入式数组或文档中的字符串?
Posted
技术标签:
【中文标题】如何匹配 MongoDB 中嵌入式数组或文档中的字符串?【英文标题】:How to match string within embedded array or doc in MongoDB? 【发布时间】:2022-01-18 10:12:04 【问题描述】:搜索了一整天后,我怀疑MongoDB是否可以满足以下要求:
问:如何过滤掉满足以下条件的文档?
在students_replies
的最后一个数组元素中,有一个学生的回复,其姓名包含字符串'ason'。
id_1: first_school, students_replies: [
Date:20210101, replies: [
name: jack, reply: 'I do not like this idea',
name: jason, reply: 'I would rather stay at home',
name: charles, reply: 'I have an plan to improve',
],
Date:20210401, replies: [
...],
Date:20210801, replies: [
...],
]
id_2: second_shool, students_replies: [..]
id_3: third_shool, students_replies: [...]
Mongoplayground
【问题讨论】:
您能否提供有效的json(使用mongoplayground.net)和预期结果?你试过$elemMatch吗? MongoDB 聚合? 感谢您的回复。我在query
和aggregation
中都尝试过$in
、$elemMatch
、$indexOfByte
,但没有一个对我有用。它们中的大多数将匹配整个值而不是其中的一部分(字符串匹配是特定的)。例如key: 'This is apple'
,我的匹配条件将在值中包含ple
,而不是值等于'This is apple'
。下面是有效的 json,预期结果将仅输出带有 key:1
和 key:3
的文档。 mongoplayground.net/p/_-MFlpzF6eY
期望的输出是什么?
喜欢这个MongoPlayground ?
您是否需要对输出文档本身也进行过滤,以便回复数组仅包含匹配的回复?
【参考方案1】:
使用$slice
和$regex
对于您的示例,这变为:
db.collection.aggregate([
// project only the last reply
"$project":
key: 1,
last_reply:
"$slice": [
"$students_replies",
-1
]
,
// filter the documents
"$match":
"last_reply.replies.name":
"$regex": "ason"
])
https://mongoplayground.net/p/a9piw2WQ8n6
【讨论】:
我好像也会输出key: 2
文档,没想到。
我听从了您的建议并制定了一个更简单的解决方案,mongoplayground.net/p/cEaiYQXq8cN,非常感谢您的帮助。
@YanTian 好的,它不输出key: 2
(不是在我运行它时),但它确实输出了除最后一个之外的其他回复,我没有抓住那部分。但是您发现为此使用$slice
。我将使用您的组合解决方案更新答案。
@YanTian 请注意,即使您在students_replies.replies.name
上有索引,由于投影的原因,它也可能不会在此处使用。出于性能原因,您可以在students_replies.replies.name
上放置一个索引,然后在students_replies.replies.name
上的$project
之前添加一个额外的$match
。这样,MongoDB 可以在不获取文档的情况下扫描正则表达式的索引,并且它还需要执行 $project
和第二个 $match
以获得更少的文档。最好尽早减少管道中的文档数量。
感谢您的见解。我完全同意It's always best to reduce the number of documents in the pipeline as early as possible
、@YuTing 也指出了这一点。由于这种最佳实践,我猜,他给出了一个非常复杂的答案,尽管他已经知道那些简单的答案。【参考方案2】:
由于您需要students_replies
的最后一个数组元素,请使用$arrayElemAt
db.collection.aggregate([
"$match":
$expr:
$regexMatch:
input:
$reduce:
input:
$arrayElemAt: [
"$students_replies.replies",
-1
]
,
initialValue: "",
in:
$concat: [
"$$value",
"$$this.name",
","
]
,
regex: "ason"
,
"$project":
"students_replies": 0
])
mongoplayground
另一个答案
db.collection.aggregate([
$match:
$expr:
$ne: [
$filter:
input:
$map:
input:
$arrayElemAt: [
"$students_replies.replies",
-1
]
,
as: "r",
in: "$$r.name"
,
as: "s",
cond:
$regexMatch:
input: "$$s",
regex: "ason"
,
[]
]
,
"$project":
"students_replies": 0
])
mongoplayground
【讨论】:
感谢您的回复,虽然看起来有点复杂,但我确实需要时间来消化您的回答。 对不起,玉婷,虽然你的答案输出了预期的结果,但我不会将其标记为答案,因为它们太复杂了,我根据@herman 的输入找到了一个更简单的答案。我在他的回答的评论中粘贴了我的解决方案。感谢您为提供帮助所付出的时间和努力。 @YanTian$match
应该始终处于聚合的顶层以实现最佳查询速度。当您拥有数千或数百万个数据时。
感谢您的指出。会记住这一点。在通过任何进一步的聚合阶段之前清除所有不需要的数据是合理的。尽管到目前为止你的答案对我来说很复杂,但我仍然从中学到了很多东西,我相信它会让我更好地为未来更高级的聚合做好准备。
@YuTing 尽管您很早就进行了过滤,但我认为这些解决方案中的正则表达式过滤器实际上不会使用索引(假设有一个)。此外,当我运行这些时,它们实际上并没有包含最后的回复。以上是关于如何匹配 MongoDB 中嵌入式数组或文档中的字符串?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 mongodb 中的聚合在嵌入文档的数组中执行操作?