$text 搜索到底怎么分词、怎么打分?这篇用 8 篇短文档验证词干、停用词、短语、排除、权重和限制。
test> db.docs.createIndex({title: "text", body: "text"}, {name: "ft"})
ft
test> db.docs.getIndexes().filter(i => i.name == "ft")
[
{
v: 2,
key: { _fts: 'text', _ftsx: 1 },
name: 'ft',
weights: { body: 1, title: 1 },
default_language: 'english',
language_override: 'language',
textIndexVersion: 3,
collation: { locale: 'simple' }
}
]文本索引的 key 是 {_fts: "text", _ftsx: 1},默认英文,每个字段权重 1。
test> db.docs.find({$text: {$search: "running"}}, {title: 1}).toArray().map(d => d._id + ":" + d.title)
[ '2:Running shoes review' ]
test> $search: "jumped" // 文档 1 里是 jumps
[ '1:The quick brown fox' ]
test> $search: "foxes" // 文档里有 fox 和 foxes
[ '1:The quick brown fox', '6:Quick fox facts' ]
test> $search: "ran" // 文档 2 里有字面的 ran
[ '2:Running shoes review' ]
test> $search: "index" // 文档里是 Indexes / index / indexing
[ '7:Database indexing', '8:Fast queries' ]
test> $search: "queries" // 文档里有 queries / query
[ '8:Fast queries', '7:Database indexing' ]搜索词和文档都做词干化:jumped 能匹配 jumps,foxes 能匹配 fox,index 能匹配 Indexes 和 indexing;不规则变化(ran 与 run)不会归一。
test> $search: "the"
0
test> $search: "the quick" // 等价于 quick
[ 6, 1, 3 ]
test> $search: "CAFE" // 大小写、重音都不敏感
[ '4:Café guide' ]
test> {$caseSensitive: true}
[]
test> {$search: "cafe", $diacriticSensitive: true}
[ '4:Café guide' ]the 这样的停用词不进索引,搜它返回 0 个;大小写和重音默认都不敏感,可以用 $caseSensitive、$diacriticSensitive 改变。
test> $search: "fox lazy" // 多个词按 OR [ 1, 5, 6 ] test> $search: "\"quick brown\"" // 短语 [ '1:The quick brown fox' ] test> $search: "quick -fox" // 排除含 fox 的 [ '3:Brown bread recipe' ]
多个词是 OR,引号里是短语,减号排除含该词的文档。
test> 默认权重(title 1、body 1)
[ '6:3.55', '1:1.33', '3:0.57' ]
test> db.docs.dropIndex("ft"); db.docs.createIndex({title: "text", body: "text"}, {name: "ft2", weights: {title: 10, body: 1}})
ft2
test> title 权重 10
[ '6:15.55', '1:13.33', '3:0.57' ]用 {$meta: "textScore"} 取分数并排序;权重让某个字段里的命中得分成倍提高。
test> try { db.docs.createIndex({body: "text"}, {name: "ft3"}) } catch (e) { e.message.slice(0, 230) }
An equivalent index already exists with a different name and options. Requested index: { v: 2, key: { _fts: "text", _ftsx: 1 }, name: "ft3", weights: { body: 1 }, default
test> 没有文本索引的集合 $text
text index required for $text query
test> $text 不在 pipeline 第一阶段
$match with $text is only allowed as the first pipeline stage
test> explain 的计划
TEXT_MATCH / IXSCAN ft2一个集合只能有一个文本索引;没有文本索引不能用 $text;聚合里 $text 必须在第一阶段。