Mongodb $text详解

时间:2024-02-22 21:30:31

在建立文本索引的集合中,使用$text,mongodb能够支持文本检索。本文结合mongodb官方文档详细描述$text的定义,用法,注意事项并实践官网给出的应用举例。

定义

$text在包含文本索引字段的集合中,执行文本检索。

语法

{
  $text: {
    $search: <string>,
    $language: <string>,
    $caseSensitive: <boolean>,
    $diacriticSensitive: <boolean>
  }
}

其中,进行文本检索时,必须在$search中指定检索关键字。其他参数都是可选参数。

  • $text定义了文本检索的方法
  • $search是字符串类型的参数,定义文本检索关键字
  • $language设置查询时用到的语言,不同的语言单词和词汇的切分可能有差异。默认使用逗号,句号,空格等标点符号进行词汇切分。 但mongodb的文本检索,不支持中文。
  • $caseSensitive, 布尔类型的变量。定义文本检索是否大小写敏感。
  • $diacriticSensitie, 布尔类型的变量。定义文本检索是否对变音词敏感。

结合mongodb官网给出的语法,给出在查询方法中文本检索的方法。

db.collection.find({
  $text: {
    $search: <string>,
    $language: <string>,
    $caseSensitive: <boolean>,
    $diacriticSensitive: <boolean>
  }
})

聚合操作中,需要将文本检索放到match方法中

db.collection
  .aggregate()
  .match({
     $text: {
      $search: <string>,
      $language: <string>,
      $caseSensitive: <boolean>,
      $diacriticSensitive: <boolean>
    }
  })

使用限制

使用mongodb进行文本检索时,有下面的限制

  • 每个查询语句中,最多只能定义一个$text表达式
  • $text查询不能出现在$nor表达式, $elemMatch查询表达式和$elemMatch投射中
  • 在$or表达式中使用$text进行文本检索时,$or表达式中所有的查询条件都要有索引。
  • 使用$text查询时,不可以使用hint指定查询时用到的索引。
  • 包含$text表达式的查询中,不能使用$natural排序
  • 不能够将文本检索与其他需要特殊索引的查询结合使用。如不可以将$text和$near操作符结合使用。
  • 视图中,不支持$text进行文本检索。
  • $text不支持使用mongodb API 创建索引。

在聚合操作中,使用$text, 有额外的限制。

  • $text表达式必须在match第一个查询条件,并且只能出现一次
  • $text表达式,不能出现在$or或者$nor表达式中
  • 文本检索结果中,默认不是按照匹配分数排列。如果需要使用匹配分数排列,需要$meta获取分数,并指定排列顺序。

$search字段

用户可以在$search字段后定义查询关键字。mongodb的$text文本检索中,more会将逗号,句号,空格等标点符号作为分词符,除了减号('-')和转移的双引号(\")。

查询关键字

在$search字段中,用户添加的查询关键字,默认会使用标点符号和空格拆分开,按照单个词语进行$or查询。如果使用用户输入的整个关键字作为查询条件,需要使用转义的双引号将查询关键字包起来。如

"\"ssl certificate\""

当没有\"时,会按照ssl和certificate两个关键字,单独查询。

当用户期望查询中只对部分字段进行自动分词时,用户可以将不需要分词的部分,使用\"包围起来。

如下面的查询条件中,ssl certificate整体做为一个查询关键字, authority, key作为单独的关键字进行查询。

"\"ssl certificate\" authority key"

排除关键字

在$search中使用减号(-),用户可以定义查询返回不包含关键字的结果。

下面的语句中,在关键字coffee前添加了减号,则表示查询出包含java或shop,但不包含coffee的文档数据。

db.stores.find({$text: {$search: "java shop -coffee"}})

排除关键字使用时,有下面两个注意事项

  • 在$search字段中,只使用减号排除字段时,文本检索不会返回任何文档
  • 在单词中的减号,不会被用作排除关键字,但会被看做分隔符号。使用减号排除关键字时,需要在减号前添加空格。

文字匹配

停止单词

在英语中,助词the,连词and等词语,在按照句子成分拆分时,会被看做拆分的符号。在mongodb文本检索中,并不会按照这样的成分进行拆分,只是按照标点符号进行简单的拆分。

词干

在大小写和注音符号不敏感的文本检索中,$text操作符匹配完整的单词。所以当一个文档中包含字段值blueberry, 使用blue作为关键字检索时,并不会返回该文档。 但使用blueberry 或blueberries会匹配出该文档。

在大小写敏感的查询中,后缀词干包含大写字母时,文本检索需要精确匹配。

在注音符号敏感的查询中,后缀词干包含注音符号或标记时,文本检索需要精确匹配。

字母敏感查询

文本检索中,指定$caseSensitive: true, mongodb匹配过程中会检查字段的字母大小写。大小写与查询关键词一致的文档才会被返回。

字母敏感查询执行顺序

当用户在文本检索中,指定大小写敏感时,即$caseSensitive: true时, 文本检索会分成下面两个过程执行。

  1. 按照用户指定的关键字,进行没有大小写敏感和注音符号的文本查询
  2. 在返回的文档当中,按照用户指定的关键字字母大小写顺序,过滤到不符合的文档。

由此可以看出,当用户指定大小写敏感时,mongodb将文本检索分成了两个过程。所以这种查询会对查询的性能产生影响。

注音符号敏感查询

与大小写敏感类似,当用户指定注音符号敏感时,mongodb的文本检索也分为了两个过程。先按照不敏感的关键字查询出结果,然后在过滤掉不符合用户关键字中注音的文档。当然,注音符号敏感也会影响mongodb文本检索的性能。

文本匹配评分

使用$text做文本检索时。mongodb会为集合中的文档文字索引计算出与查询关联词中的相关性评分。使用{$meta: "textScore"},可以获得这个评分信息。

应用举例

创建集合articles, 并添加文本索引

db.articles.createIndex({subject: "text"})

db.articles.insertMany([
     { _id: 1, subject: "coffee", author: "xyz", views: 50 },
     { _id: 2, subject: "Coffee Shopping", author: "efg", views: 5 },
     { _id: 3, subject: "Baking a cake", author: "abc", views: 90  },
     { _id: 4, subject: "baking", author: "xyz", views: 100 },
     { _id: 5, subject: "Café Con Leche", author: "abc", views: 200 },
     { _id: 6, subject: "Сырники", author: "jkl", views: 80 },
     { _id: 7, subject: "coffee and cream", author: "efg", views: 10 },
     { _id: 8, subject: "Cafe con Leche", author: "xyz", views: 10 }
    ])

单个单词查询

db.articles.find({$text: {$search: "coffee"}})

匹配多个单词,mongodb默认使用空格,逗号等将用户输入的"bake coffee cake"拆分成3个关键字并进行or查询,匹配到任意一个关键字的文档都会被查询出来

db.articles.find({$text: {$search: "bake coffee cake"}})

使用转义双引号\",匹配带有标点符号和空格的查询关键字。使用转义双引号将关键字包围起来,mongodb不会对查询关键字进行拆分,而是作为整体去查询。

db.articles.find({$text: {$search: "\"coffee shop\""}})

使用转义的单引号,mongodb还是对查询关键字进行拆分。两个查询返回结果一致

db.articles.find({$text: {$search: "\'coffee shop\' \'cafe con Leche\'"}})

db.articles.find({$text: {$search: "coffee shop cafe con Leche"}})

使用减号,排除关键字

db.articles.find({$text: {$search: "coffee -shop"}})

指定文本检索时使用的语言

db.articles.find({$text: {$search: "leche", $language: "es"}})

使用带有注音标记符的关键字查询

db.articles.find({$text: {$search: "сы́рники CAFÉS"}})

大小写敏感查询

db.articles.find({$text: {$search: "Coffee", $caseSensitive: true}})

db.articles.find({$text: {$search: "\"Café Con Leche\"", $caseSensitive: true}})

db.articles.find({$text: {$search: "Coffee -shop", $caseSensitive: true}})

注音标记符敏感查询

db.articles.find( { $text: { $search: "CAFÉ", $diacriticSensitive: true } } )

db.articles.find(
  { $text: { $search: "leches -cafés", $diacriticSensitive: true } }
)

获取mongodb计算的相似度

db.articles.find(
    { $text: {$search: "cake"}},
    { score: {$meta: "textScore"}}
    )

按照相似度排序,排序是,score字段名称可以是任意合法名称

db.articles.find(
    { $text: {$search: "coffee"}},
    { score: {$meta: "textScore"}}
).sort({ score: {$meta: "textScore"}})

按照相似度排序并限制返回数量

db.articles.find(
    { $text: {$search: "coffee"}},
    { score: {$meta: "textScore"}}
).sort({ score: {$meta: "textScore"}})
 .limit(2)

添加其他查询条件

db.articles.find(
    {author: 'xyz', $text: {$search: "coffee"}},
    { score: {$meta: "textScore"}}
).sort({date: 1, score: {$meta: "textScore"}})
 .limit(2)

聚合操作中使用文本检索

db.articles.aggregate(
    [
        {$match: {$text: { $search: "cake tea"}}},
        {$sort: { score: {$meta:"textScore"}}},
        {$project: {subject: 1, _id: 0}}
    ]
)

db.articles.aggregate(
    [
        {$match: {$text: { $search: "cake tea"}}},
        {$project: {subject: 1, _id: 0, score:  { $meta: "textScore"}}},
        {$match: {score: {$gt: 0.7}}}
    ]
)

db.articles.aggregate(
    [
        {$match: {$text: { $search: "cake tea", $language: 'es'}}},
        {$project: {subject: 1, _id: 0, score:  { $meta: "textScore"}}},
        {$match: {score: {$gt: 0.7}}}
    ]
)