用 ES 做搜索,最常见的两个问题是「搜不到」和「搜出一堆不相关的」。这两个问题的根源往往都在分词和索引设计上。
倒排索引是什么
普通数据库是「文档 → 包含哪些词」,倒排索引反过来:「词 → 出现在哪些文档」。查「数据库」时直接拿到包含这个词的文档列表,不需要逐篇扫描。
词项 文档 ID
数据库 [1, 5, 9]
索引 [5, 9]
优化 [1, 9]
写入时把文本切成词项(分词),查询时把查询串也切成词项,然后求交集。所以分词规则必须一致——索引时怎么切,查询时就得怎么切。
中文为什么要单独处理
英文按空格切就行,中文没有空格。默认的 standard 分词器会把一整句中文切成单字,导致「数据库」被拆成「数」「据」「库」,搜「数据」也能命中,但相关性很差。
用 IK 分词器:
PUT /articles
{
"settings": {
"analysis": {
"analyzer": {
"ik_smart_analyzer": { "type": "custom", "tokenizer": "ik_smart" }
}
}
},
"mappings": {
"properties": {
"title": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart" },
"content": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart" },
"created": { "type": "date" },
"views": { "type": "integer" }
}
}
}
关键点:索引时用 ik_max_word(细粒度,召回全),查询时用 ik_smart(粗粒度,更精准)。这个组合是中文搜索的常规配置。
验证分词结果
GET /articles/_analyze
{ "analyzer": "ik_max_word", "text": "MySQL 索引优化实战" }
输出会告诉你切成哪些词。搜不到的时候先跑这个,八成能看出问题。
相关性
ES 默认用 BM25 打分,考虑词频、逆文档频率、字段长度。标题和正文权重不同,用 multi_match 加 boost:
{
"query": {
"multi_match": {
"query": "索引优化",
"fields": ["title^3", "content"],
"type": "best_fields"
}
}
}
title^3 表示标题命中权重是正文的三倍。
常见坑
- mapping 不能改。字段类型定了就不能改,要改只能重建索引 + reindex。所以一开始就把 mapping 想清楚,别靠动态映射。
- 深分页。
from + size超过 10000 会报错,用search_after或 scroll。 - 聚合字段用 keyword。
text字段不能直接聚合,要加.keyword子字段或者单独建 keyword 类型。 - 不要当主存储。ES 的数据可靠性不如数据库,丢了不好恢复,主数据必须在 MySQL 里。
一句话
ES 好用,但它不是「装了就能搜」。分词器、mapping、查询方式三样配好,才有符合预期的效果。
- THE END -
最后修改:2024年6月8日
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://mi-blog.cn/archives/684
共有 0 条评论