计算文档相关性的算法:TF-IDF(词频-逆文档频率)
其他可以定制化的地方:标题关键字的权重比正文更高,点赞数高的权重高,发版时间越新的权重越高
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oUa1akpK-1661087430227)(img/倒排索引.png)]
分析器都有三部分组成:字符过滤器、分词器、分词过滤器,其中字符过滤器和分词过滤器可以有多个,分词器只有一个
建立索引和查询需要用相同的分析器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1oDiO03t-1661087430228)(img/分析器.png)]
POST /_analyze
{
"analyzer": "standard", # 直接指定分析器
"text": "The letter tokenizer is not configurable. letter"
}
POST /index/_analyze
{
"field": "title", # 使用title字段的分析器来分析
"text": "The letter tokenizer is not configurable. letter"
}
# 自定义分析器
POST /_analyze
{
"tokenizer": "standard", # 分词器用standard
"filter": ["lowercase"], #分词过滤器用lowercase
"text": "The letter tokenizer is not configurable. letter"
}
# setting参数中设置分析器的作用于所有text字段
PUT /index
{
"setting": {
"analysis": {
"analyzer": {
"default": {
"type": "simple"
}
}
}
}
}
# mapping中设置分析器作用于单个字段
PUT /index
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "whitespace", #索引时使用的分析器
"search_analyzer" #搜索时使用的分析器,不配置则默认和analyzer一致,一般用于同义词搜索的情况
}
}
}
}
安装地址:/{ES_HOME}/plugins,下载ik解压即可
下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
ik提供了ik_max_word和ik_smart两种分析器,区别是ik_max_word切分粒度比较细,ik_smart粒度比较粗
添加新词:创建文件并添加新词,然后在config/IKAnalysis.cfg.xml将新建的字典文件加入ext_dict
安装和使用方式ik类似
synonyms是ES内置的分词过滤器,它是支持用户自定义同义词的分词过滤器;
synonyms_graph它和synonyms用法类似,区别是synonyms用于索引,而synonyms_graph用于搜索
PUT /index
{
"setting": {
"analysis": {
"filter": { # 自定义过滤器
"ik_synonyms_filter": {
"type": "synonym",
"synonyms_path": "synonyms.dict"
}
},
"analyzer": {
"ik_analyzer_synonyms": { # 自定义分析器
"tokenizer": "ik_max_word",
"filter": ["lowercase", "ik_synonyms_filter"]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_analyzer_synonyms" # 使用自定义分析器
}
}
}
}
# 重新加载自定义分析器,触发加载同义词库
POST /index/_reload_search_analyzers
# standard分析器指定停用词
PUT /index
{
"setting": {
"analysis": {
"analyzer" {
"my_standard": {
"type": "standard",
"stopwords": ["我", "的", "这"]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "my_standard"
}
}
}
}
${ES_HOME}/plugins/ik-analysis/config/IKAnalyzer.cfg.xml配置文件里面可以指定停用词
${ES_HOME}/plugins/hanlp-analysis/data/dictionary/stopwords.txt里面添加停用词
地址:https://github.com/medcl/elasticsearch-analysis-pinyin,安装方式同其他分析器安装一样
ES中的文档比数据库的表更加灵活,表只能存一层的数据,而ES文档的值可以是数组也可以是更多的键值对,他鼓励你将属于一个实体的数据保存在一个文档中
文档对应数据库的行,类型对应数据库的表,索引对应数据库的库
字段类型自动检测可能导致报错,比如,先来个7 es自动检测为数字,如果后面再来个hello world就会报错
新建一个文档不是马上对查询可见的,有个refresh_interval的设置,默认1秒,索引刷新操作很昂贵所以有这样一种设计考虑,因此elasticsearch也被称为准实时的
一个索引会分成多个分片(默认5个),每个分片可以分布在不同的存储节点上,分片的es处理的最小单元,每个分片就是一个Lucene索引:包含倒排索引的文件目录;每个分片默认一个副分片本,副本分片和主分片不在同一节点上面,副本分片可以服务于搜索请求,并且在主分片无法访问时时升级成主分片;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FbQ9VWHV-1661087430229)(img/节点与分片.png)]
副本分片可以在运行的时候进行添加和移除,而主分片不可以
可以在任何时候改变每个分片的副本分片的数量,因为副本分片总是可以被创建和移除。这并不适用于索引划分为主分片的数量,在创建索引之前,你必须决定主分片的数量。请记住,过少地分片将限制可扩展性,但是过多的分片会影响性能。默认设置的5份是一个不错的开始。
类型只是逻辑上的分离,在ES中不同类型的文档并没有物理上的分离,在一个ES索引中的所有文档其实都存储在相同分片的同一组文件中。 所以在同一个索引的不同类型中相同的字段必须有相同的数据类型。
首先根据文档ID计算其hash值,然后确认其应该存储在的节点,接收请求的节点将请求转发到主节点简历主分片,随后在所有备份节点上建立副本分片,主分片和副本分片都建立好后返回成功。
搜索索引的过程主要就是接收请求的节点把请求转发到索引所在分片的节点,这个转发过程可以做负载均衡,使用轮训机制选择可用的分片(可能是主分片也可能副本分片),ES从这些分片搜集结果将其聚集成功单一结果返回给客户端
curl - XPUT 'localhost:9200/index/_mapping' -d '{
"properties": {
"next_time": {
"type": "date",
"format": "MMM DD YYYY"
}
}
}'
_ttl: 剩余存活时间
_timestamp: 创建时间
_all:表示所有字段
推荐使用kibana的dev tools作为和ES进行交互式的请求工具,本地安装方式地址:http://localhost:5601
ES老板有类型的概念,新版本去除了这个概念,只有一个默认的"_doc"
term/terms(完全匹配):可以用于keyword、数字、boolean、date、数组
GET /index/_search
{
"query": {
"term": {
"city": {
"value" : "北京"
}
}
}
}
GET /index/_search
{
"query": {
"terms": {
"city": ["北京", "上海"]
}
}
}
match(分词匹配):用于text查询
GET /index/_search
{
"_source": ["title"],
"query": {
"match": {
"title": "金都酒店"
}
}
}
或者
GET /index/_search
{
"_source": ["title"],
"query": {
"match": {
"title": {
"query": "金都酒店",
"boost": 2, # 设置该字符串的查询权重,可以影响得分和排序
}
}
}
}
multi_match:在多个字段上使用match
GET /index/_search
{
"_source": ["title"],
"query": {
"multi_match": {
"query": "金都酒店",
"fields": ["title", "amenities"]
}
}
}
match_all:查询所有
GET /index/_search
{
"query": {
"match_all": {
"boost": 1 # 设置所有文档得分
}
}
}
range:一般用于date、数字类型,gt大于,lt小于,gte大于等于,lte小于等于
GET /index/_search
{
"query": {
"match": {
"price": {
"gte": 300,
"lte": 500
}
}
}
}
suggest(前缀匹配):搭配completion类型使用
GET /index/_search
{
"suggestion": {
"hotel_zh_sug": { # 定义搜索建议名称
"prefix": "如家",
"completion": {
"field": "field_name"
}
}
}
}
exists(查询存在某个字段的文档)
GET /index/_search
{
"query": {
"exists": {
"field": "tag" # 查询存在tag字段的文档
}
}
}
geo_distance:基于地理位置查询
GET /index/_search
{
"_source": ["title", "city", "location"],
"query": {
"geo_distance": {
"distance": "5km", # 搜索距离不超过5km的酒店
"location": { # 搜索字段
"lat": "39.915143", # 维度
"lon": "116.4039" # 经度
}
}
}
}
geo_bounding_box:搜索指定地区矩形区域内的文档
GET /index/_search
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": { # 左上角坐标
"lat": "39.922921",
"lon": "116.457044"
},
"right_bottom": {
"lat": "39.907104",
"lon": "116.479466"
}
}
}
}
}
geo_polygon:搜索指定多边形区域内的文档
GET /index/_search
{
"query": {
"geo_polygon": {
"location": {
"points": [{
"lat": "111",
"lon": "222"
},{
"lat": "333",
"lon": "444"
},{
"lat": "555",
"lon": "666"
}]
}
}
}
}
PUT /index
{
"settings": {
"number_of_shards": 15,
"number_of_replicas": 2
},
"mappings": {
"properties": {
"title": {
"type": "text"
},
"city": {
"type": "keyword"
},
"price": {
"type": "double"
},
"create_time": {
"type": "date"
},
"full_room": {
"type": "boolean"
},
"location": {
"type": "geo_point"
},
"tags": {
"type": "keyword"
},
"comment_info": {
"properties": {
"favourable_comment": {
"type": "integer"
},
"negative_comment": {
"type": "integer"
}
}
}
}
}
}
PUT /index/_setting
{
"analysis": {
"filter": { # 自定义过滤器
"ik_synonyms_filter": {
"type": "synonym",
"synonyms_path": "synonyms.dict"
}
},
"analyzer": {
"ik_analyzer_synonyms": { # 自定义分析器
"tokenizer": "ik_max_word",
"filter": ["lowercase", "ik_synonyms_filter"]
}
}
}
}
DELETE /index
POST /index/_close
POST /index/_open
curl -XPOST 'localhost:9200/index' -H 'content-type:application/json' -d
{
"mappings:" {
"properties": {
"name": {
"type": "text",
"index": true
},
"city": {
"type": "keyword"
},
"price": {
"type": "double"
}
}
}
}
可以多次定义映射,并且新的映射会和老的映射自动合并;但是无法改变已有的字段类型和索引方式
curl 'localhost:9200/index/_mapping/?pretty'
# id可以指定也可以不指定
POST /hotel/_doc/{id}
{
"title": "weiyela",
"city": "chengdu",
"price": 298.99
}
POST /index/_update/id
{
"doc": {
"title": "好再来酒店",
"city": "北京",
"price": 659.45
}
"upsert": { # 可有可没有,表示没有的时候执行插入
"title": "好再来酒店",
"city": "北京",
"price": 659.45
}
}
curl 'localhost:9200/index/id?pretty'
通过ID查询是实时的,而通过搜索查询只是近实时的,保存后可能有1秒左右延时才能搜索
POST /_bulk
{"index": {"_index": "index_name"}} # 写入索引
{...} # 写入文档内容
{"index": {"_index": "index_name", "_id": "id"}} # 指定id
{...}
{"update": {"_index": "index_name", "_id": "id"}} # 更新指定id文档
{"doc": {"title": "文雅豪情酒店", "city": "北京"}} # 更新内容
{"delete": {"_index": "index_name", "_id": "id"}} # 更新指定id文档
POST /_bulk
{"index": {"_index": "hotel", "_id": "001"}}
{"title": "文雅酒假日酒店", "city": "北京", "price": 556.00, "create_time": "20200418120000", "full_room": true, "location": {"lat": 39.938838, "lon": 116.448112}, "tags": ["wifi", "小型电影院"], "comment_info": {"favourable_comment": 20, "negative_comment": 10}}
{"index": {"_index": "hotel", "_id": "002"}}
{"title": "金都嘉怡假日酒店", "city": "北京", "create_time": "20200315200000", "full_room": false, "location": {"lat": 39.915153, "lon": 116.4030}, "tags": ["wifi", "免费早餐"], "comment_info": {"favourable_comment": 20, "negative_comment": 10}}
{"index": {"_index": "hotel", "_id": "003"}}
{"title": "金都假日酒店", "city": "北京", "price": 200.00, "create_time": "20210509160000", "full_room": true, "location": {"lat": 40.002096, "lon": 116.3766773}, "comment_info": {"favourable_comment": 20, "negative_comment": 10}}
{"index": {"_index": "hotel", "_id": "004"}}
{"title": "金都假日酒店", "city": "天津", "price": 500.00, "create_time": "20210218080000", "full_room": false, "location": {"lat": 39.155004, "lon": 117.203976}, "tags": ["wifi", "免费车位"]}
{"index": {"_index": "hotel", "_id": "005"}}
{"title": "文雅精选酒店", "city": "天津", "price": 800.00, "create_time": "202101010800", "full_room": true, "location": {"lat": 39.178447, "lon": 117.219999}, "tags": ["wifi", "充电车位"], "comment_info": {"favourable_comment": 20, "negative_comment": 10}}
POST /index/_update_by_query
{
"query": { # 如果不要则是更新所有文档
"term": {
"city": {
"value": "北京"
}
}
},
"script": {
"source": "ctx._source['city']='上海'",
"lang": "painless"
}
}
DELETE /index/_doc/id
POST /index/_delete_by_query
{
"query": {
"term": {
"city": {
"value": "北京"
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JJ4gWmOc-1661087430229)(img/搜索语法.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R3HgtKHJ-1661087430230)(img/搜索结果.png)]
# 搜索多个索引
curl 'localhost:9200/index,index2/_search?q=es&pretty'
# 搜索所有索引
curl 'localhost:9200/_search?q=es&pretty'
GET /index/_search
{
"explain": true, # 是否返回查询分析
"_source": ["title", "city"],
"query": {
"term": {
"city": {
"value": "北京"
}
}
},
"from": 0, # 分页查询起势偏移量,从零开始
"size": 20, # 分页大小,最大10000,超过则报错
"profile": "true", #开启性能剖析开关
"sort": [{ # 有sort后,查询返回将没有得分score(null)
"price": {
"order": "desc"
}
}],
"sort": [{
"_geo_distance": {
"location": { # 设置排序的中心点坐标
"lat": "39.915143",
"lon": "116.4039"
},
"order": "asc", # 由近及远排序
"unit": "km",
"distance_type": " plane " # 排序所使用的距离计算算法
}
}],
"highlight": { # 高亮显示
"fields": {
"title": {
"type": "plain" # plain高精确度,需要载入内存重新查询分析,适用于短字段;unified是默认值;fvh速度快,可使用于大字段(超过1MB)
"pre_tags": "" , # 默认是
"post_tags": ""
}
}
}
}
分页查询中,每个分片节点都是构建一个from+size长度的有序队列,然后协调节点将个分片节点数据汇总,则需要N * (from+size)长度的队列用于全局排序
GET /index/_count
{
"query": {...}
}
GET /index/_explain/id
{
"query": {...}
}
虽然boost值可以对查询的权重进行调整,但是仅限于term查询和match查询,如果是其他查询类型却不行,这时就可以用到boosting查询
GET /index/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"title": {
"query": "金都"
}
}
},
"negative": { # 设置负面查询
"range": {
"price": {
"lte": 200
}
}
},
"negative_boost": 0.2 # 设置降低的权重值
}
}
}
GET /index/_search
{
"size": 0, # 表示不查询
"aggs": { # 聚合查询
"my_agg": { # 聚合名称
"avg": { # 求平均值
"field": "price"
}
}
}
}
GET /index/_search
{
"size": 0,
"aggs": { # 聚合查询
"my_agg": { # 聚合名称
"stats": { # 文档数量、最大值、最小值、平均值、加和值一并返回
"field": "price"
}
}
}
}
GET /index/_search
{
"size": 0,
"aggs": { # 聚合查询
"my_agg": { # 聚合名称
"value_count": { # 类似count,但是不统计空值的文档,如果用于数组则所有文档数量中非空的总和
"field": "price"
}
}
}
}
GET /index/_search
{
"size": 0,
"aggs": { # 聚合查询
"my_agg": { # 聚合名称
"sum": { # 类似count,但是不统计空值的文档,如果用于数组则所有文档数量中非空的总和
"field": "price",
"missing": 0 # 空值当做0处理
}
}
}
}
GET /hotel/_search
{
"size": 0,
"aggs": {
"my_agg": {
"terms": { # 按城市分组,只适用于keyword、bool、keyword数组字段
"field": "city"
}
}
}
}
GET /hotel/_search
{
"size": 0,
"aggs": {
"my_agg": {
"range": { # 按数组进行分组
"field": "city",
"ranges": [{
"to": 200 # 不指定from,默认0
}, {
"from": 200,
"to": 500
}, {
"from": 500 # 不指定to,默认为该字段最大值
}]
}
}
}
}
GET /hotel/_search
{
"size": 0,
"aggs": {
"my_agg": {
"terms": { # 按城市分组,只适用于keyword、bool、keyword数组字段
"field": "city"
},
"aggs": { # 子分组,可以无线嵌套
"my_sum": {
"sum": {
"field": "price",
"missing": 0
}
}
}
}
}
}
GET /hotel/_search
{
"size": 0,
"aggs": {
"my_agg": {
"geo_distance": {
"field": "location",
"origin": {
"lat": 39.915143,
"lon": 116.4039
},
"unit": "km", # mi米, km千米
"ranges": [{
"to": 3
}, {
"from": 3,
"to": 10
}, {
"from": 10
}]
}
}
}
}
GET /hotel/_search
{
"size": 0,
"query": {
"term": {
"city": {
"value": "北京"
}
}
},
"aggs": {
"my_agg": {
"avg": {
"field": "price"
}
}
}
}
GET /hotel/_search
{
"size": 0,
"query": {
"term": {
"city": {
"value": "天津"
}
}
},
"aggs": {
"my_agg": {
"filter": { # 指定前过滤器
"term": {
"full_room": false
}
},
"aggs": {
"my_avg": {
"avg": {
"field": "price"
}
}
}
}
}
}
GET /hotel/_search
{
"size": 0,
"query": {
"match": {
"title": "假日"
}
},
"post_filter": { #后过滤器,在聚合完成后对查询起作用,所以不影响聚合,只影响查询
"term": {
"city": "北京"
}
},
"aggs": {
"my_agg": {
"avg": {
"field": "price",
"missing": 200
}
}
}
}
GET /hotel/_search
{
"size": 0,
"aggs": {
"query_city": {
"terms": {
"field": "city",
"order": {
"_count": "asc" # 按count的升序排序
}
},
"aggs": {
"my_avg": {
"avg": {
"field": "price",
"missing": 200
}
}
}
}
}
}
GET /hotel/_search
{
"size": 0,
"aggs": {
"group_city": {
"terms": {
"field": "city",
"order": {
"my_avg": "asc"
}
},
"aggs": {
"my_avg": {
"avg": {
"field": "price",
"missing": 200
}
}
}
}
}
}
GET /hotel/_search
{
"size": 0,
"aggs": {
"query_city": {
"terms": {
"field": "city",
"order": {
"_key": "asc" # 按key升序排序
}
},
"aggs": {
"my_avg": {
"avg": {
"field": "price",
"missing": 200
}
}
}
}
}
}
ES支持同时返回查询结果和聚合结果
GET /hotel/_search
{
"size": 0,
"query": {
"match": {
"title": "金都"
}
},
"aggs": {
"group_city": {
"terms": {
"field": "city"
},
"aggs": {
"my_avg": {
"top_hits": { # 只返回每个父分组的前N个
"size": 3,
"sort": [{
"uuid": {
"order": "desc"
}
}
]
}
}
}
}
}
}
GET /hotel/_search
{
"from": 0, # 当有collapse时,作用于每个分组内
"size": 5, # 当有collapse时,作用于每个分组内
"query": {
"match": {
"title": "金都"
}
},
"collapse": { # collapse方法只支持对某一个字段去重
"field": "city"
}
}