MongoDB
中的 text
索引支持全文搜索。这种类型的 text
索引不应该与 MongoDB Atlas
全文搜索索引full-text search index
相混淆,不同于 MongoDB
文本索引,后者利用 Apache Lucene
来提供额外的文本搜索功能。如果应用程序允许用户提交关键字查询,而这些查询与集合中的标题、描述和其他字段的文本相匹配,那么应该使用 text
索引。
文本索引支持对包含字符串内容的字段进行文本搜索查询。文本索引可提高搜索字符串内容中特定单词或短语时的性能。
一个集合只能有一个文本索引,但该索引可以包含多个字段。
文本索引支持对包含字符串内容的字段进行文本搜索查询。文本索引可提高搜索字符串内容中特定单词或短语时的性能。
创建文本索引需使用db.collection.createIndex()
方法。语法如下:
db.<collection>.createIndex(
{
<field1>: "text",
<field2>: "text",
...
}
)
对于某些集合,你可能并不知道文档包含哪个字段。可以使用 $**
在文档的所有字符串字段上创建全文本索引。这样做不仅会对顶层的字符串字段建立索引,也会搜索内嵌文档和数组中的字符串字段。语法如下:
db.<collection>.createIndex( { "$**": "text" } )
传播关键如下集合:
> db.blog.insertMany( [
{
_id: 1,
content: "This morning I had a cup of coffee.",
about: "beverage",
keywords: [ "coffee" ]
},
{
_id: 2,
content: "Who likes chocolate ice cream for dessert?",
about: "food",
keywords: [ "poll" ]
},
{
_id: 3,
content: "My favorite flavors are strawberry and coffee",
about: "ice cream",
keywords: [ "food", "dessert" ]
}
] )
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3 } }
在content
字段上创建文本索引:
> db.blog.createIndex( { "content": "text" } )
content_text
该索引支持在 content
字段上进行文本搜索查询。例如,以下查询返回其中 content
字段包含 coffee
字符串的文档:
> db.blog.find({ $text: { $search: "coffee" } })
[
{
_id: 1,
content: 'This morning I had a cup of coffee.',
about: 'beverage',
keywords: [ 'coffee' ]
},
{
_id: 3,
content: 'My favorite flavors are strawberry and coffee',
about: 'ice cream',
keywords: [ 'food', 'dessert' ]
}
]
{ "content": "text" }
索引仅包含content
字段,不返回非索引字段的匹配项。例如,以下查询在blog
集合中搜索字符串food
:
> db.blog.find(
{
$text: { $search: "food" }
}
)
查询未返回任何文档。尽管字符串 food
出现在文档 _id: 2
和 _id: 3
中,但它分别出现在字段 about
和 keywords
中。about
和 keywords
字段不包含在文本索引中,因此不会影响文本搜索查询结果。
在 blog
集合的 about
和 keywords
字段上创建复合文本索引:
> db.blog.createIndex({ "about": "text", "keywords": "text" })
about_text_keywords_text
该索引支持对 about
和 keywords
字段进行文本搜索查询。例如,以下查询返回字符串 food
出现在 about
或 keywords
字段中的文档:
> db.blog.find({ $text: { $search: "food" } })
[
{
_id: 3,
content: 'My favorite flavors are strawberry and coffee',
about: 'ice cream',
keywords: [ 'food', 'dessert' ]
},
{
_id: 2,
content: 'Who likes chocolate ice cream for dessert?',
about: 'food',
keywords: [ 'poll' ]
}
]
在blog
集合上创建通配符文本索引:
> db.blog.createIndex( { "$**": "text" } )
$**_text
查询blog
集合中的字符串coffee
:
> db.blog.find( { $text: { $search: "coffee" } } )
[
{
_id: 1,
content: 'This morning I had a cup of coffee.',
about: 'beverage',
keywords: [ 'coffee' ]
},
{
_id: 3,
content: 'My favorite flavors are strawberry and coffee',
about: 'ice cream',
keywords: [ 'food', 'dessert' ]
}
]
$text
会使用空格和大多数标点符号作为分隔符来对搜索字符串进行标记,并在搜索字符串时对所有这些标记执行 OR
逻辑。
查询blog
集合中包含字符串poll
或coffee
的文档:
> db.blog.find( { $text: { $search: "poll coffee" } } )
[
{
_id: 1,
content: 'This morning I had a cup of coffee.',
about: 'beverage',
keywords: [ 'coffee' ]
},
{
_id: 3,
content: 'My favorite flavors are strawberry and coffee',
about: 'ice cream',
keywords: [ 'food', 'dessert' ]
},
{
_id: 2,
content: 'Who likes chocolate ice cream for dessert?',
about: 'food',
keywords: [ 'poll' ]
}
]
查询blog
集合中包含短语chocolate ice cream
的文档:
> db.blog.find( { $text: { $search: "\"chocolate ice cream\"" } } )
[
{
_id: 2,
content: 'Who likes chocolate ice cream for dessert?',
about: 'food',
keywords: [ 'poll' ]
}
]
默认情况下,文本索引的 default_language
为 english
。为提高非英语文本搜索查询的性能,可以指定与文本索引相关的不同默认语言。语法如下:
db.<collection>.createIndex(
{ <field>: "text" },
{ default_language: <language> }
)
如果指定default_language
值为none
,则文本索引会解析字段中的每个单词,包括停用词,并忽略后缀词干。
创建如下集合:
> db.quotes.insertMany( [
{
_id: 1,
quote : "La suerte protege a los audaces."
},
{
_id: 2,
quote: "Nada hay más surrealista que la realidad."
},
{
_id: 3,
quote: "Es este un puñal que veo delante de mí?"
},
{
_id: 4,
quote: "Nunca dejes que la realidad te estropee una buena historia."
}
] )
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4 } }
以下操作在 quote
字段上创建文本索引,并将 default_language
设置为 spanish
:
> db.quotes.createIndex({ quote: "text" }, { default_language: "spanish" } )
quote_text
生成的索引支持使用西班牙语后缀词干提取规则对 quote
字段进行文本搜索查询。例如,以下查询在 quote
字段中搜索关键字 punal
:
> db.quotes.find(
{
$text: { $search: "punal" }
}
)
[ { _id: 3, quote: 'Es este un puñal que veo delante de mí?' } ]
虽然 $search
值设置为 punal
,但查询将返回包含单词 puñal
的文档,因为文本索引对变音符不敏感。
该索引还忽略了特定语言的停止词。例如,虽然带有 _id: 2
的文档包含词语 hay
,但以下查询不会返回任何文档。hay
被归类为西班牙语停止词,即不包含在文本索引中。
为每个索引字段创建具有不同权重的 text
索引:
> db.blog.createIndex(
{
content: "text",
keywords: "text",
about: "text"
},
{
weights: {
content: 10,
keywords: 5
},
name: "BlogTextIndex"
}
)
BlogTextIndex
text
索引有以下字段和权重:
这些权重表示索引字段之间的相对重要性。
以下查询在 blog
集合中的文档中搜索字符串 ice cream
:
db.blog.find(
{
$text: { $search: "ice cream" }
},
{
score: { $meta: "textScore" }
}
).sort( { score: { $meta: "textScore" } } )
[
{
_id: 2,
content: 'Who likes chocolate ice cream for dessert?',
about: 'food',
keywords: [ 'poll' ],
score: 12
},
{
_id: 3,
content: 'My favorite flavors are strawberry and coffee',
about: 'ice cream',
keywords: [ 'food', 'dessert' ],
score: 1.5
}
]
搜索字符串 ice cream
匹配:
content
字段中术语匹配的影响力(10:1
权重)是 keywords
字段中术语匹配的 10
倍。
以下查询在 blog
集合中的文档中搜索字符串 food
:
> db.blog.find(
{
$text: { $search: "food" }
},
{
score: { $meta: "textScore" }
}
).sort( { score: { $meta: "textScore" } } )
[
{
_id: 3,
content: 'My favorite flavors are strawberry and coffee',
about: 'ice cream',
keywords: [ 'food', 'dessert' ],
score: 5.5
},
{
_id: 2,
content: 'Who likes chocolate ice cream for dessert?',
about: 'food',
keywords: [ 'poll' ],
score: 1.1
}
]
搜索字符串 food
匹配:
keywords
字段中术语匹配的影响力(5:1
权重)是 about
字段中术语匹配的 5
倍。
为了在搜索字符串匹配多个字段时计算 score
,MongoDB 会将匹配的字段数量乘以相应字段的权重,然后对结果求和。
如果对大型数据集执行文本搜索查询,单字段文本索引可能会扫描大量条目以返回结果,这可能会导致查询速度变慢。
如要提高查询性能,您可以创建复合文本索引 ,并在文本搜索查询中包含等值匹配。如果复合索引包含等值匹配中使用的字段,索引将扫描更少条目,并更快返回结果。
创建如下集合:
> db.inventory.insertMany( [
{ _id: 1, department: "tech", description: "lime green computer" },
{ _id: 2, department: "tech", description: "wireless red mouse" },
{ _id: 3, department: "kitchen", description: "green placemat" },
{ _id: 4, department: "kitchen", description: "red peeler" },
{ _id: 5, department: "food", description: "green apple" },
{ _id: 6, department: "food", description: "red potato" }
] )
{
acknowledged: true,
insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6 }
}
在包含以下字段的 inventory
集合上创建复合索引:
> db.inventory.createIndex(
{
department: 1,
description: "text"
}
)
department_1_description_text
创建复合索引后,文本搜索查询仅扫描与 department
字段上指定的相等条件匹配的文档。
例如,以下查询会扫描 department
等于 kitchen
的文档,其中 description
字段包含字符串 green
:
> db.inventory.find( { department: "kitchen", $text: { $search: "green" } } )
[ { _id: 3, department: 'kitchen', description: 'green placemat' } ]