组合 MongoDB 列的未排序搜索

Posted

技术标签:

【中文标题】组合 MongoDB 列的未排序搜索【英文标题】:Unsorted search of combined MongoDB columns 【发布时间】:2021-09-09 01:55:51 【问题描述】:

是否可以搜索由两列组成的虚拟列

假设我有以下 MongoDB 集合:

db.collection = 
[
     book : 'The Stand',   author : 'Stephen King',
     book : 'The Dead Zone',   author : 'Stephen King',
     book : 'Hamlet',   author : 'William Shakespeare',
     book : 'The Tragedy of Othello',   author : 'William Shakespeare',
     book : 'Danse Macabre',   author : 'Stephen King',
]

我想同时考虑bookauthor 列进行搜索。特别是,我将有一个查询字符串,其中包含由空格分隔的多个项目,并且我希望返回其联合 book+author 列包含所有查询项目的文档,而不管它们的顺序如何。

例子:

查询:“国王”

 book : 'The Stand',   author : 'Stephen King',
 book : 'The Dead Zone',   author : 'Stephen King'

查询:“悲剧莎士比亚”

 book : 'The Tragedy of Othello',   author : 'William Shakespeare'

查询:“那个”

 book : 'The Stand',   author : 'Stephen King',
 book : 'The Dead Zone',   author : 'Stephen King',
 book : 'The Tragedy of Othello',   author : 'William Shakespeare',

在 MongoDB 中可以进行这种搜索吗?是否有任何$regex 表达式使其可行?

谢谢!

【问题讨论】:

提供可以复制和粘贴以进行测试的有效 JSON 非常宝贵。感谢您创建这些有效的 JSON 对象 - 包括源记录以及预期结果。 【参考方案1】:

这是一个我认为可能会有所帮助的聚合......

db.collection.aggregate([
     $project:  book: 1, author: 1, "book_words":  $split: [ "$book", " " ] , "author_words":  $split: [ "$author", " " ]   ,
     $project:  book:1, author: 1, "search_words":  $concatArrays: [ "$book_words", "$author_words" ]   ,
     $match:  "search_words":  $all: [ "The", "King" ]   ,
     $project:  "search_words": 0 
]).pretty()

说明:

这个聚合有 4 个阶段...

    $项目 $项目 $匹配 $项目

第一个 $project 会将字段“book”中的字符串值拆分为一个名为“book_words”的单词数组,并将字段“author”中的字符串值拆分为一个名为“author_words”的单词数组

第二个 $project 将两个新数组连接到一个名为“search_words”的数组中

$match 阶段过滤掉与搜索条件不匹配的记录

最后的 $project 阶段删除了名为“search_words”的临时数组字段

此聚合的结果文档看起来像...


    "_id" : ObjectId("60d6139a9148371ae7d2b343"),
    "book" : "The Stand",
    "author" : "Stephen King"


    "_id" : ObjectId("60d6139a9148371ae7d2b344"),
    "book" : "The Dead Zone",
    "author" : "Stephen King"

不区分大小写的匹配

为了提供不区分大小写的匹配,MongoDB 必须了解不区分大小写的含义。英文大小写不同于其他语言。因此,出于这个原因,我们必须添加一个索引,其排序规则将英语定义为语言,排序规则的强度为 2——这意味着英语不区分大小写。创建索引后,我们必须将排序规则作为聚合中的一个选项来引用。

创建索引

db.collection.createIndex(  book: 1, author: 1 ,  collation:  locale: 'en', strength: 2   )

这是两个字段的复合索引 - 'book' 和 'author'。注意此索引的排序选项...

使用排序规则进行聚合

现在索引以特定的排序规则存在,Mongo 现在可以计算不区分大小写的选项...

db.collection.aggregate([
     $project:  book: 1, author: 1, "book_words":  $split: [ "$book", " " ] , "author_words":  $split: [ "$author", " " ]   ,
     $project:  book:1, author: 1, "search_words":  $concatArrays: [ "$book_words", "$author_words" ]   ,
     $match:  "search_words":  $all: [ "the", "king" ]   ,
     $project:  "search_words": 0 
],
 collation:  locale: "en", strength: 2  ).pretty()

请注意,排序选项应用于聚合。此外,聚合 $match 阶段现在使用所有小写文本。

这是输出...


    "_id" : ObjectId("60d6139a9148371ae7d2b343"),
    "book" : "The Stand",
    "author" : "Stephen King"


    "_id" : ObjectId("60d6139a9148371ae7d2b344"),
    "book" : "The Dead Zone",
    "author" : "Stephen King"

小心

使用带有排序规则选项的正则表达式可能无法按预期工作,至少从索引策略的角度来看是这样。在我的示例中,我没有使用任何正则表达式 ($regex),因此它按预期工作。但同样,这是针对完全匹配,而不是部分匹配(也称为范围查询),例如“以 'ki*' 开头”

MongoDB Atlas 搜索

如果使用 MongoDB Atlas,则使用 Atlas Search 可以直接解决此问题,但省略了“the”等常用词。

【讨论】:

非常感谢!这绝对超出了我目前的知识范围。我稍后会测试它:) 是否可以使 $match 不区分大小写?也许使用 $toLower?我无法得到它:( @VíctorMartínez - 请在我的回答中查看不区分大小写选项的更新。

以上是关于组合 MongoDB 列的未排序搜索的主要内容,如果未能解决你的问题,请参考以下文章

mongoDB中的名字和姓氏组合搜索

如何在 MongoDB 中搜索动态字段并首先对最佳匹配结果进行排序

CosmosDB mongoDB API 排序和全文搜索抛出错误

应该针对不同的排序和过滤条件创建哪些MongoDB索引来提高性能?

MongoDB 条件查询和排序

带有排序的 MongoDB 范围查询 - 如何加快速度?