RestFul操作ElasticSearch:索引与文档全攻略

发布于:2025-05-21 ⋅ 阅读:(13) ⋅ 点赞:(0)

RestFul方式操作ES

索引库操作

创建索引库

PUT /索引库名称
{
	"mappings":{
		"properties":{
			"字段名":{
				"type":"字段类型",
				"analyzer":"分词器",
				"index":"是否参与搜索(布尔值)"
			},
			...
		}
	}
}

查询索引库

GET /索引库名

删除索引库

/DELETE /索引库名

修改索引库(增加字段)

ES中是禁止修改索引库的,因为修改索引库的操作会导致整个索引库变得不可用。

虽然不支持直接修改,但是支持增加字段。

PUT /索引库名/_mapping
{
	"properties":{
		"新字段名":{
			"type":"字段类型",
			"analyzer":"分词器",
			"index":"是否参与搜索(布尔值)"
		},
		...
	}
}

文档操作

新增文档

POST /索引库名/_doc/文档id
{
	"字段1":"值1",
	"字段2":"值2",
	"嵌套字段3":{
		"子字段1":"值3",
		"子字段2":"值4"
	},
}

新增文档的请求路径为/索引库名/_doc,文档id可以不指定,如果不指定会由ES自动生成。

删除文档

DELETE /索引库名/_doc/文档id

修改文档

修改文档有两种方式:

  • 全量修改:会完全覆盖原有内容。如果id存在则是先删除旧的数据再添加新的数据,不存在则是直接新增。
  • 增量修改:修改指定字段的内容。
POST /索引库名/_doc/文档id
{
	"字段1":"值1",
	"字段2":"值2",
	"嵌套字段3":{
		"子字段1":"值3",
		"子字段2":"值4",
		...
	},
	...
}
POST /索引库名/_update/文档id
{
	"doc":{
		"要修改的字段名":"修改的值",
		...
	}
}

查询文档

GET /索引库名/_doc/文档id

DSL查询

DSL查询的基本语句如下:

GET /索引库名/_search
{
    "query":{
        "查询类型":{ //支持的各种查询类型,例如 match、term、range 等。
            "字段名":"查询值"
        }
    }
}

查询所有

查询该索引库下的所有数据一般用match_all,本身是没有任何查询条件的。

GET /索引库名/_search
{
    "query":{
        "match_all":{}
    }
}

全文检索

全文检索是利用用户输入的内容分词,根据倒排索引进行匹配。

match:只允许查询一个字段

multi_match:允许查询多个字段

GET /索引库名/_search
{
	"query":{
		"match":{
			"字段":"搜索值"
		}
	}
}
GET /索引库名/_search
{
	"query":{
		"multi_match":{
			"query":"搜索值",
			"fields":["字段1","字段2",..."字段n"]
		}
	}
}

精确查询

根据精确词条值查值,一般是keyword、数值、日期、布尔等类型的字段。

range:根据范围进行精确查找,可以是数值,也可以是日期。

term:根据搜索条件精确匹配

GET /索引库名/_search
{
	"query":{
		"range":{
			"字段名":{
				"gte":最低值,
				"lte":最高值
			}
		}
	}
}
GET /索引库名/_search
{
	"query":{
		"term":{
			"字段名":{
				"value":"精确值"
			}
		}
	}
}

地理查询

根据坐标经纬度查询。

geo_distance:查询指定中心点小于某个距离的所有文档。

geo_bounding_box:查询某个坐标点落在某个范围的所有文档。

GET /索引库名/_search
{
	"query":{
		"geo_distance":{
			"distance":"距离值",
			"经纬度字段":"当前经纬度坐标"
			}
		}
	}
}
GET /索引库名/_search
{
	"query":{
		"geo_bounding_box":{
			"字段名":{
				"top_left":{
					"lat":最小值,
					"lon":最大值
				},
				"bottom_right":{
					"lat":最小值,
					"log":最大值
				}
			}
		}
	}
}

复合查询

⭐ES中计算得分的方式是基于计算词频的统计计算。早期采用了TF-IDF算法,但是这种算法在词频无限高的时候得分也会趋于无限高。所以后期采用了BM25算法,这种新的算法虽然也是基于词频,但是在词频无限高的时候它会趋于一种平稳的趋势。

复合查询可以将多种查询条件组合到一块进行查询。

bool:布尔查询是一个或多个查询子句的组合。子查询的组合方式有:

  • must:必须匹配每个子查询,类似于"与"
  • should:选择性匹配子查询,类似于"或"
  • must_not:必须不匹配,不参与算分,类似于"非"
  • filter:必须匹配,不参与算法

function_score:可以修改文档的相关性算分。

GET /索引库名/_search
{
	"query":{
		"bool":{
			"must":[
				"match":{
					"字段名":"搜索值"
				},
				...
			],
			"should":[...],
			"must_not":[...],
			"filter":[]
		}
	}
}
GET /索引库名/_search
{
	"query":{
		"function_score":{
			"query":{
				"match":{
					"字段名":"搜索值"
				}
			},
			"functions":[
				{
					"filter":{
						"term":{
							"字段名":"匹配值"
						}
					},
					"weight":字段得分权重值
				}
			],
			"boost_mode":"multiply"
		}
	}
}

其中的query部分是查询条件

function中是影响算分的相关信息,此例中代表对filter筛选出的文档进行调整算分权重,而别的文档不管。

boost_mode是加权模式,这个例子中代表将原始得分×10得出新的分数

查询结果处理

排序

ES支持对搜索结果排序,默认是根据相关度得分进行排序,如果自己指定了排序规则的话,就不会进行相关性得分的计算,查询效率可能更优。

可以用来排序的字段类型有:keyword、数值、地理坐标、日期等。

排序规则可以直接在查询中的"sort"中配置。

GET /索引库名称/_search
{
	"query":{
		"match_all":{}
	},
	"sort":[
		{
			"排序字段名1":"排序规则" //asc  desc
		},
		...
	]
}

分页

ES默认只返回前10条数据,如果要查询更多的话就需要用到分页查询了,分页查询会先查到from+size条数据,然后进行排序,最后返回size条数据。

GET /索引库名称/_search
{
	"query":{
		"match_all":{}
	},
	"from":分页开始位置,
	"size":获取多少条数据
}

由于分页查询需要查询from+size条数据,并且还涉及到排序,所以对于深度分页,效率并不高。

ES中做了设定最大分页数不能超过10000。

对此ES对深度分页提出了两个解决方案:

search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。但是它知识拿到了上一次最后一条数据的排序值,缺点是只能向后查。(官方推荐使用的方式)

SCro:原理将排序数据形成快照,保存在内存。缺点明显在数据更新时对缓存的处理,官方已经不推荐使用。

高亮

在搜索的时候我们需要在搜索结果中把搜索关键字突出显示。

ES可以做到这件事,原理是:通过对关键字添加标签,在页面中对标签添加css样式即可。

GET /索引库名称/_search
{
	"query":{
		"match_all":{}
	},
	"heightlight":{
		"fields":{
			"要高亮的字段名1":{
				"pre_tags":"<em>",
				"post_tags":"</em>"
			},
			...
		}
	}
}

聚合

聚合可以实现对文档的统计、分析、运算等操作。常见的聚合有三类:

  • 桶聚合:用来对文档做分组。
    • TermAggeregation:按照文档字段值分组
    • Date Histogram:按照日期阶梯分组
  • 度量聚合:用于计算一些值,例如:
    • Avg:平均值
    • Max:最大值
    • Min:最小值
    • Stats:同时求max、min、avg、sum等。
  • 管道聚合:以其它聚合的结果为基础做聚合。

使用"aggs"来定义聚合相关的属性,而"size"是指定相关文档的数量。如果只希望拿到聚合结果,那么size可以为0。

桶-字段聚合

GET /索引库名称/_search
{
	"query":{ //聚合前对数据的处理
		"match_all":{}
	},
	"size":相关文档的数量,
	"aggs":{
		"聚合的名称":{ //给聚合取个名称,查询结果中会返回该聚合名称下的聚合结果
			"terms":{ //聚合的类型,这里是桶聚合下的字段聚合
				"field":"聚合的字段名",
				"size":希望获取的结果数量
			}
		}
	}
}

度量聚合

GET /索引库名称/_search
{
	"query":{ //聚合前对数据的处理
		"match_all":{}
	},
	"size":相关文档的数量,
	// 下面的写法是嵌套聚合,先根据外层条件进行聚合,之后根据内层条件进行再次聚合
	"aggs":{
		"聚合的名称":{ //给聚合取个名称,查询结果中会返回该聚合名称下的聚合结果
			"terms":{ //聚合的类型,这里是桶聚合下的字段聚合
				"field":"聚合的字段名",
				"size":希望获取的结果数量
			},
			"aggs":{
				"聚合的名称":{ //给聚合取个名称,查询结果中会返回该聚合名称下的聚合结果
					"stats":{ //聚合的类型,这里的stats可以计算min、max、avg等
						"field":"聚合的字段名"
					}
				}
			}
		}
	}
}

自动补全查询

创建索引库

分词器的选择

我们在浏览器中使用的自动补全功能首先是基于拼音去做自动补全的,其次才是汉字,所以我们还需要学习拼音分词器。

拼音分词器官网:https://github.com/medcl/elasticsearch-analysis-pinyin

安装拼音分词器插件后就可以配置到plugin目录中,重启es即可使用。

拼音分词器使用方式是,在创建索引库的时候指定"analyzer":"pinyin"即可。

仅仅安装了拼音分词器还不足以支持自动补全,因为拼音分词器很弱智,只能分成拼音,而且还没有汉字。

所以我们就需要实现自定义分词器。

创建索引时候的分词器

elasticsearch中分词器(analyzer)的组成包含三部分:

  • character filters:在tokenizer之前对文本进行处理。例如删除字符、替换字符以及表情。
  • tokenizer:将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik smart
  • tokenizer filter:将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等

只有在创建索引库的时候才能创建自定义分词器,在其中指定分词器包含的三个组成部分即可(可以只指定几个部分),而三个组成部分也可以自定义。

PUT /索引库名称
{
	"settings":{
		"analysis":{
			"analyzer":{ //自定义分词器
				"自定义分词器名称":{
					"character":"ik_max_word",
					// "tokenizer":"", //可以设置部分
					"filter":"pinyin"
				}
			},
			"filter":{ // 自定义tokenizer filter
				"自定义tokenizer filter名称":{
					"type":"过滤器类型",
					// 下面是一些自定义的配置
					...
				}
			}
		}
	},
	"mappings":{
		"properties":{
			"字段名":{
				"type":"字段类型",
				"analyzer":"创建索引时使用的分词器", //这边就可以使用上面设置的自定义拼音分词器了
				"search_analyzer":"搜索时候使用的分词器", //一般用ik_smart分词器
				"index":"是否参与搜索(布尔值)"
			},
			...
		}
	}
}

自定义分词器直接CV即可,傻子才自己写!!!!

搜索时候的分词器

:::color4
自定义的拼音分词器只有在创建索引的时候适合用,并不适合在搜索的时候也使用,这是因为如果我们搜一个中文,它会把中文拆成拼音,再去根据拼音找文档,这样找出来的文档准确定非常低。所以我们需要在创建索引库的时候分别指定sanalyzer和search_analyzer。

:::

索引库的完善

elasticsearch提供了Completion Suggesteri查询来实现自动补全功能。这个查询会匹配以用户输入内容开头的词条并返回。为了提高补全查询的效率,对于文档中字段的类型有一些约束:

  • 参与补全查询的字段必须是completion类型
  • 字段的内容一般是用来补全的多个词条形成的数组
PUT /索引库名称
{
	"mappings":{
		"properties":{
			"字段名":{
				"type":"completion",
				"analyzer":"创建索引时使用的分词器", //这边就可以使用上面设置的自定义拼音分词器了
			},
			...
		}
	}
}

而在插入的时候:

POST /索引库名/_doc
{
	"字段名":["词条1","词条2",..."词条3"]
}

自动补全使用

区别于DSL查询使用"query",自动补全的使用方式有所不同

GET /索引库名/_search
{
    "suggest":{
        "查询名称":{ //自定义查询名称
            "查询字段":"查询内容",
            "completion":{
                "field":"补全查询的字段",
                "skip_duplicates":true, //跳过重复的
                "size":10 //获取前10条结果
            }
        }
    }
}

RestClient方式操作

想要在Java中操作ES,官方提供了RestClient的依赖,当然也可以使用spring-boot-starter-data-elasticsearch相关的依赖。

使用RestClient操作ES的步骤如下:

  1. 导入依赖
<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
  1. 锁定版本
<properties>
  <java.version>1.8</java.version>
  <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
  1. 初始化RestClient
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ElasticsearchConfig {

    @Bean(destroyMethod = "close")
    public RestHighLevelClient elasticsearchClient() {
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("localhost", 9200, "http")));
        return client;
    }
}    

ES数据同步

es数据同步采用rabbitmq,因为rabbitmq本身是可靠的。

在增删改的时候发送消息给ES,去让ES进行同步。


网站公告

今日签到

点亮在社区的每一天
去签到