5 分钟启一个 ES
单节点本地学习用。先确认 vm.max_map_count 足够大(ES 默认推荐 262144),Linux 上:
sudo sysctl -w vm.max_map_count=262144
docker-compose 启动 7.17(最后一个 7.x LTS):
services:
es:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.10
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m
- xpack.security.enabled=false
ports:
- "9200:9200"
ulimits:
memlock: { soft: -1, hard: -1 }
健康检查:
curl http://localhost:9200/_cluster/health
中文分词:IK
默认分词器对中文不友好(会把每个字拆开)。装 IK 插件:
docker exec -it es bash
./bin/elasticsearch-plugin install \
https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.10/elasticsearch-analysis-ik-7.17.10.zip
exit
docker restart es
两个分词模式:
ik_max_word:最细粒度拆分(适合索引时)ik_smart:粗粒度拆分(适合查询时)
Mapping 与字段类型
ES 7 起,type 概念被弱化(一个 index 只有一个 _doc)。建一个商品索引:
PUT /products
{
"mappings": {
"properties": {
"title": { "type": "text", "analyzer": "ik_max_word",
"fields": { "keyword": { "type": "keyword" } } },
"brand": { "type": "keyword" },
"price": { "type": "double" },
"stock": { "type": "integer" },
"tags": { "type": "keyword" },
"created_at": { "type": "date" }
}
}
}
要点:
text字段会被分词,keyword字段不会;常用双字段(title+title.keyword)兼顾全文检索与精确过滤。- Mapping 一旦建好,已存在字段的类型不可改。要改只能 reindex 到新 index。
- 日期字段支持
date、date_nanos,默认 ISO-8601。
查询 DSL
基础查询
# 全文匹配(分词后匹配)
GET /products/_search
{
"query": { "match": { "title": "运动 鞋" } }
}
# 精确匹配(不分词)
GET /products/_search
{
"query": { "term": { "brand": "nike" } }
}
# 短语匹配(保持顺序、紧邻)
GET /products/_search
{
"query": { "match_phrase": { "title": "篮球鞋" } }
}
复合查询
GET /products/_search
{
"query": {
"bool": {
"must": [ { "match": { "title": "跑鞋" } } ],
"filter": [ { "range": { "price": { "gte": 100, "lte": 500 } } } ],
"must_not": [ { "term": { "tags": "停产" } } ]
}
}
}
must:参与打分filter:只过不过滤、不打分,更快must_not:反向排除
高亮
GET /products/_search
{
"query": { "match": { "title": "运动鞋" } },
"highlight": { "fields": { "title": {} } }
}
返回里每个 hit 的 highlight.title 会有 <em> 包裹的命中片段。
聚合
按品牌分桶求平均价格:
GET /products/_search
{
"size": 0,
"aggs": {
"by_brand": {
"terms": { "field": "brand", "size": 10 },
"aggs": {
"avg_price": { "avg": { "field": "price" } }
}
}
}
}
size: 0:不返回文档,只返回聚合结果terms:分桶聚合,类似 SQL 的GROUP BYavg/sum/min/max/stats:指标聚合
关联建模
nested:一对多嵌套
一个用户有多个地址,地址里要按省/市分别过滤:
PUT /users
{
"mappings": {
"properties": {
"login_name": { "type": "keyword" },
"addresses": {
"type": "nested",
"properties": {
"province": { "type": "keyword" },
"city": { "type": "keyword" }
}
}
}
}
}
GET /users/_search
{
"query": {
"nested": {
"path": "addresses",
"query": { "term": { "addresses.province": "上海" } }
}
}
}
join:父子关系
父子必须在同一 index(ES 限制):
PUT /ecom
{
"mappings": {
"properties": {
"category_name": { "type": "text", "analyzer": "ik_max_word",
"fields": { "keyword": { "type": "keyword" } } },
"join_field": {
"type": "join",
"relations": { "category": "product" }
}
}
}
}
查询父下所有子:
GET /ecom/_search
{
"query": {
"has_child": {
"type": "product",
"query": { "range": { "price": { "gte": 5000, "lte": 10000 } } }
}
}
}
实战经验
- 深分页别用
from + size大数,改用search_after或滚动 API。 - 默认 10000 条上限,超限会报
result_window_too_large,调大index.max_result_window是最后手段。 - 建索引时一次性把 mapping 设对,改 mapping 代价大。
- 日期字段排序慢,考虑
date+ keyword 双字段做精确查找。
使用准则
- ES 7+ 起不再需要 type;新建索引直接用
_doc。 - 中文检索必装 IK;索引
ik_max_word,查询ik_smart。 - 聚合前确认字段是
keyword或数值类型——text字段直接聚合会报错。 - nested 字段查询必须包在
nestedquery 里,否则退化为扁平数组匹配。