倒排索引
倒排索引:将各个文档中的内容,进行分词,形成词条。然后记录词条和数据的唯一标识(id)的对应关系,形成的产物。
ElasticSearch数据的存储和搜索原理

使用数据库查询存在的问题
- 性能低:使用模糊查询,左边有通配符,不会走索引,会全表扫描,性能低
- 功能弱:如果以”华为手机“作为条件,查询不出来数据
使用ElasticSearch

- 使用“手机”作为关键字查询
- 生成的倒排索引中,词条会排序,形成一颗树形结构,提升词条的查询速度
- 使用“华为手机”作为关键字查询
- 华为 :1,3
- 手机 :1,2,3
ElasticSearch概念
- ElasticSearch是一个基于Lucene的搜索服务器
- 是一个分布式、高扩展、高实时的搜索与数据分析引擎
- 基于RESTful web接口
- Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎
- 官网:https://www.elastic.co/
- MySQL有事务性,而ElasticSearch没有事务性,所以你删了的数据是无法恢复的
- ElasticSearch没有物理外键这个特性,,如果你的数据强一致性要求比较高,还是建议慎用
ElasticSearch和MySql分工不同,MySQL负责存储数据,ElasticSearch负责搜索数据
应用场景
- 搜索:海量数据的查询
- 日志数据分析
- 实时数据分析
ElasticSearch 核心概念
索引(index)
- ElasticSearch存储数据的地方,可以理解成关系型数据库中的数据库概念。
映射(mapping)
- mapping定义了每个字段的类型、字段所使用的分词器等。相当于关系型数据库中的表结构.
文档(document)
- Elasticsearch中的最小数据单元,常以json格式显示。一个document相当于关系型数据库中的一行数据。
倒排索引
- 一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,对应一个包含它的文档id列表
RESTful风格
- 表述性状态转移,是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是RESTful。就是一种定义接口的规范
- 基于HTTP
- 可以使用XML格式定义或JSON格式定义
- 每一个URI代表1种资源
- 客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作
- GET:用来获取资源
- POST:用来新建资源(也可以用于更新资源)
- PUT:用来更新资源
- DELETE:用来删除资源
添加索引
- PUT http://ip:端口/索引名称
查询索引
- GET http://ip:端口/索引名称 # 查询单个索引信息
- GET http://ip:端口/索引名称1,索引名称2… # 查询多个索引信息
- GET http://ip:端口/_all # 查询所有索引信息
删除索引
- DELETE http://ip:端口/索引名称
关闭索引
- POST http://ip:端口/索引名称/_close
打开索引
- POST http://ip:端口/索引名称/_open
操作映射
数据类型
- 简单数据类型
- 字符串
- text:会分词,不支持聚合
- keyword: 不会分词,将全部内容作为一个词条,支持聚合
- 数值

- 布尔 boolean
- 二进制 binary
- 范围类型 integer_range, float_range, long_range, double_range, date_range
- 日期 date
- 字符串
- 复杂数据类型
- 数组:[]
- 对象:{}
GET goods_index
# 创建索引
PUT person
# 查询索引
GET person
#只查询映射
GET person/_mapping
# 添加映射
PUT person/_mapping
{
"properties":{
"name":{
"type":"keyword"
},
"age":{
"type":"integer"
}
}
}
#删除索引
DELETE person
# 创建索引并添加映射
PUT person
{
"mappings": {
"properties": {
"name":{
"type":"keyword"
},
"age":{
"type":"integer"
}
}
}
}
PUT person/_mapping
{
"properties":{
"address":{
"type":"text"
}
}
}
总结

PUT person/_doc/1
{
"name":"张三",
"age":"19",
"address":"重庆"
}
# 查询文档
GET person/_doc/1
GET person/_doc/4YNAV3cBWsFGw7fPpne7
# 添加文档,不指定id
POST person/_doc/2
{
"name":"李四",
"age":19,
"address":"渝北"
}
# 修改文档
PUT person/_doc/2
{
"name":"王五",
"age":30,
"address":"成都"
}
#根据id查询数据
GET person/_doc/2
#查询所有文档
GET person/_search
#根据id删除文档
DELETE person/_doc/4YNAV3cBWsFGw7fPpne7
分词器
- 分词器(Analyzer):将一段文本,按照一定逻辑,分析成多个词语的一种工具
- 如:华为手机 — > 华为、手、手机
- ElasticSearch 内置分词器
- Standard Analyzer - 默认分词器,按词切分,小写处理
- Simple Analyzer - 按照非字母切分(符号被过滤), 小写处理
- Stop Analyzer - 小写处理,停用词过滤(the,a,is)
- Whitespace Analyzer - 按照空格切分,不转小写
- Keyword Analyzer - 不分词,直接将输入当作输出
- Patter Analyzer - 正则表达式,默认\W+(非字符分割)
- Language - 提供了30多种常见语言的分词器
ElasticSearch 内置分词器对中文很不友好,处理方式为:一个字一个词
IK分词器
- IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包
- 是一个基于Maven构建的项目
- 具有60万字/秒的高速处理能力
- 支持用户词典扩展定义
- 下载地址:https://github.com/medcl/elasticsearch-analysis-ik/archive/v7.4.0.zip
# ik 分词器,粗粒度分词器
GET _analyze
{
"analyzer": "ik_smart",
"text": "我爱北京天安门"
}
# ik 分词器 ,细粒度分词器
GET _analyze
{
"analyzer": "ik_max_word",
"text": "我是程序员"
}
查询文档
词条查询:term
- 词条查询不会分析查询条件,只有当词条和查询字符串完全匹配时才匹配搜索
# term 词条查询。查询的条件和词条完全匹配
# es 默认使用的分词器是standard,一个字,一个词
GET person/_search
{
"query": {
"term": {
"address": {
"value": "北"
}
}
}
}
全文查询:match
• 全文查询会分析查询条件,先将查询条件进行分词,然后查询,求并集
# match 先会对查询的字符串进行分词,在查询。求交集
GET person/_search
{
"query": {
"match": {
"address": "重庆午餐"
}
}
}
SpringBoot整合ES
- 应如es版本的坐标
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.4.0</version>
</dependency>
- 配置文件
elasticsearch:
host: 159.75.102.92
port: 9200
- 定义配置类
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticSearchConfig {
private String host;
private int port;
@Bean
public RestHighLevelClient client() {
return new RestHighLevelClient(RestClient.builder(new HttpHost(
host,
port,
"http"
)));
}
}
- 测试
@Autowired
private RestHighLevelClient client;
@Test
void contextLoads() {
System.out.println(client);
}
添加索引
/**
* 添加索引
*/
@Test
public void testAddIndex() throws IOException {
//1.使用client获取操作索引对象
IndicesClient indicesClient = client.indices();
//2.具体操作,获取返回值
CreateIndexRequest createRequest = new CreateIndexRequest("itheima");
CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);
//3.根据返回值判断结果
System.out.println(response.isAcknowledged());
}
@Test
public void testAddIndexAndMapping() throws IOException {
//1.使用client获取操作索引对象
IndicesClient indicesClient = client.indices();
//2.具体操作,获取返回值
CreateIndexRequest createRequest = new CreateIndexRequest("itcast");
//2.1设置mappings
String mapping="{\n" +
" \"properties\" : {\n" +
" \"address\" : {\n" +
" \"type\" : \"text\",\n" +
" \"analyzer\" : \"ik_max_word\"\n" +
" },\n" +
" \"age\" : {\n" +
" \"type\" : \"long\"\n" +
" },\n" +
" \"name\" : {\n" +
" \"type\" : \"keyword\"\n" +
" }\n" +
" }\n" +
" }";
createRequest.mapping(mapping,XContentType.JSON);
CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);
//3.根据返回值判断结果
System.out.println(response.isAcknowledged());
}
查询索引
/**
* 查询索引
*/
@Test
public void queryIndex() throws IOException {
IndicesClient indicesClient = client.indices();
GetIndexRequest getIndexRequest = new GetIndexRequest("itcast");
GetIndexResponse response = indicesClient.get(getIndexRequest, RequestOptions.DEFAULT);
//获取结果
Map<String, MappingMetaData> mappings = response.getMappings();
for (String key : mappings.keySet()) {
System.out.println(key+":"+mappings.get(key).getSourceAsMap());
}
}
删除索引
/**
* 删除索引
*/
@Test
public void deleteIndex() throws IOException {
IndicesClient indicesClient = client.indices();
DeleteIndexRequest deleteRequest = new DeleteIndexRequest("itheima");
AcknowledgedResponse response = indicesClient.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(response.isAcknowledged());
}
判断索引是否存在
/**
* 判断索引是存在
*/
@Test
public void existIndex() throws IOException {
IndicesClient indicesClient = client.indices();
GetIndexRequest getRequest=new GetIndexRequest("itheima");
boolean exists = indicesClient.exists(getRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
添加文档
/**
* 添加文档,使用map作为数据
*/
@Test
public void addDoc() throws IOException {
//数据对象,map
Map<String, Object> data = new HashMap<>();
data.put("address", "重庆");
data.put("name", "张国荣");
data.put("age", 20);
//获取操作文档的对象
IndexRequest request = new IndexRequest("itcast").id("1").source(data);
//添加数据,获取结果
IndexResponse response = client.index(request, RequestOptions.DEFAULT);
//打印响应结果
System.out.println(response.getId());
}
/**
* 添加文档,使用对象作为数据
*/
@Test
public void addDoc2() throws IOException {
//数据对象java object
Person person=new Person();
person.setId("2");
person.setAddress("北京");
person.setAge(33);
person.setName("张三");
ObjectMapper objectMapper=new ObjectMapper();
//将对象转为json
String data = objectMapper.writeValueAsString(person);
//获取操作文档的对象
IndexRequest indexRequest=new IndexRequest("itcast").id(person.getId()).source(data,XContentType.JSON);
//添加数据,获取结果
IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
System.out.println(response.getId());
}
修改文档
- 添加文档时,如果id存在则修改,id不存在则添加
查询文档
/**
* 根据id查询文档
*/
@Test
public void testFindById() throws IOException {
GetRequest getRequest = new GetRequest("itcast", "2");
GetResponse response = client.get(getRequest, RequestOptions.DEFAULT);
//获取数据对应的json
System.out.println(response.getSourceAsString());
}
删除文档
/**
* 根据id删除文档
*/
@Test
public void testDeleteDoc() throws IOException {
DeleteRequest deleteRequest=new DeleteRequest("itcast","2");
DeleteResponse response = client.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(response.getId());
}
ElasticSearch高级操作
批量操作-脚本
- Bulk 批量操作是将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数。
# 批量操作
#1.删除3号记录
#2.添加4记录
#3.修改2号记录名称
POST _bulk
{"delete":{"_index":"person","_id":"3"}}
{"create":{"_index":"person","_id":4}}
{"name":"八号","age":80,"address":"北京"}
{"update":{"_index":"person","_id":"1"}}
{"doc":{"name":"一号"}}
JavaAPI批量操作
@Autowired
private RestHighLevelClient client;
/**
* 批量操作 bulk
*/
@Test
public void testBulk() throws IOException {
//创建bulkrequest对象
BulkRequest bulkRequest = new BulkRequest();
/*
* 批量操作
* 1.删除1号记录
* 2.添加5记录
* 3.修改2号记录名称
*/
//添加对应的操作
//1.删除1号记录
DeleteRequest deleteRequest = new DeleteRequest("person", "1");
bulkRequest.add(deleteRequest);
//2.添加5号记录
Map<String, Object> map = new HashMap<>();
map.put("name", "长江五号");
IndexRequest indexRequest = new IndexRequest("person" ).id("5").source(map);
bulkRequest.add(indexRequest);
//3.修改4号记录
Map<String, Object> map1 = new HashMap<>();
map.put("name", "诸葛亮");
UpdateRequest updateRequest = new UpdateRequest("person", "4").doc(map1);
bulkRequest.add(updateRequest);
//执行批量操作
BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
RestStatus status = response.status();
System.out.println(status);
}
将数据库数据导入ES
/**
* 批量添加数据
*/
@Test
public void importData() throws IOException {
//1.查询所有的数据
List<Goods> goodsList = goodsMapper.findAll();
//2.bulk导入
BulkRequest bulkRequest = new BulkRequest();
//3.遍历goodsList,创建IndexRequest添加数据
for (Goods goods : goodsList) {
//4.设置spec的 map数据
goods.setSpec(JSON.parseObject(goods.getSpecStr(), Map.class));
IndexRequest indexRequest = new IndexRequest("goods");
indexRequest.id(goods.getId() + "").source(JSON.toJSONString(goods), XContentType.JSON);
bulkRequest.add(indexRequest);
}
client.bulk(bulkRequest,RequestOptions.DEFAULT);
}
matchAll查询 ->查询所有文档
- 脚本
# 默认情况下,es一次展示10条数据,通过form和size来控制分页
GET goods/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 100
}
- JavaAPI
/**
* 查询所有
* 1.matchAll
* 2.将查询结果封装为Goods对象,装在到list中
* 3.分页,默认显示10条
*/
@Test
public void testMatchAll() throws IOException {
//2.构建查询请求对象,指定查询的索引名称
SearchRequest searchRequest = new SearchRequest("goods");
//4.创建查询条件器这个对象 SearchSourceBuilder
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//6.查询条件
QueryBuilder query = QueryBuilders.matchAllQuery();
//5.指定查询->查询所有文档
sourceBuilder.query(query);
//3.查询条件构建器
searchRequest.source(sourceBuilder);
//10.添加分页信息
sourceBuilder.from(0);
sourceBuilder.size(100);
//1.查询,获取查询结果
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
//7.获取命中对象 SearchHits
SearchHits searchHits = response.getHits();
//8.获取总记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录为:" + value);
//9.获取Hits数据 数组
SearchHit[] hits = searchHits.getHits();
List<Goods> goodsList = new ArrayList<>();
for (SearchHit hit : hits) {
//获取json字符串格式的对象
String sourceAsString = hit.getSourceAsString();
//转为java对象
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
}
term查询—>不会对查询条件进行分词
- 脚本
#term查询
GET goods/_search
{
"query": {
"term": {
"title": {
"value": "手机"
}
}
}
}
- JavaApI
/**
* termQuery:词条查询
*/
@Test
public void testTermQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
QueryBuilder query = QueryBuilders.termQuery("title", "华为");
sourceBuilder.query(query);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = searchResponse.getHits();
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);
List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
}
match查询
- 会对查询条件进行分词
- 然后将分词后的查询条件和词条进行等值匹配
- 默认取并集(OR)
脚本
# match查询
GET goods/_search
{
"query": {
"match": {
"title": "华为"
}
}
}
GET goods/_search
{
"query": {
"match": {
"title": {
"query": "华为手机",
"operator": "and"
}
}
}
}
JavaAPI
/**
* matchQuery:词条查询
*/
@Test
public void testMatchQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
MatchQueryBuilder query = QueryBuilders.matchQuery("title", "华为手机");
query.operator(Operator.AND);
sourceBuilder.query(query);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = searchResponse.getHits();
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);
List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
}
模糊查询
- wildcard查询:会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)
- regexp查询:正则查询
- prefix查询:前缀查询
脚本
# wildcard查询。 查询条件分词,模糊查询
GET goods/_search
{
"query": {
"wildcard": {
"title": {
"value": "华*"
}
}
}
}
# regexp查询:正则查询
GET goods/_search
{
"query": {
"regexp": {
"title": "\\w+(.)*"
}
}
}
# prefix查询:前缀查询
GET goods/_search
{
"query": {
"prefix": {
"brandName": {
"value": "三"
}
}
}
}
JavaAPI
/**
* 模糊查询:WildcardQuery
*/
@Test
public void testWildcardQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
WildcardQueryBuilder query = QueryBuilders.wildcardQuery("title", "华为");
sourceBuilder.query(query);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = searchResponse.getHits();
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);
List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
}
/**
* 正则查询: RegexpQuery
*/
@Test
public void testRegexpQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
RegexpQueryBuilder query = QueryBuilders.regexpQuery("title", "\\w+(.)*");
sourceBuilder.query(query);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = searchResponse.getHits();
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);
List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
}
/**
* 前缀查询: prefixQuery
*/
@Test
public void testPrefixQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
PrefixQueryBuilder query = QueryBuilders.prefixQuery("brandName", "三");
sourceBuilder.query(query);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = searchResponse.getHits();
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);
List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
}
范围查询->查找指定字段在指定范围内包含值
- 脚本
# 范围查询
# 范围查询
GET goods/_search
{
"query": {
"range": {
"price": {
"gte": 2000,
"lte": 3000
}
}
},
"sort": [
{
"price": {
# 升叙排序
"order": "asc"
}
}
]
}
- JavaAPI
/**
* 范围查询:rangeQuery
*/
@Test
public void testRangeQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//范围查询
RangeQueryBuilder query = QueryBuilders.rangeQuery("price");
//指定下限>=
query.gte(2000);
//指定上限<=
query.lte(3000);
sourceBuilder.query(query);
//排序
sourceBuilder.sort("price", SortOrder.ASC);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = searchResponse.getHits();
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);
List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
}
queryString查询
- 会对查询条件进行分词
- 然后将分词后的查询条件和词条进行等值匹配
- 默认取并集(OR)
- 可以指定多个查询字段
脚本
# queryString 支持 AND 和OR
GET goods/_search
{
"query": {
"query_string": {
"fields": ["title","categoryName","brandName"],
"query": "华为AND 手机"
}
}
}
# simple_query_string 不支持AND 和OR
GET goods/_search
{
"query": {
"simple_query_string": {
"fields": ["title","categoryName","brandName"],
"query": "华为AND 手机"
}
}
}
JavaAPI
/**
* 条件查询: queryString
*/
@Test
public void testQueryStringQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为手机").field("title").field("categoryName").field("brandName").defaultOperator(Operator.AND);
sourceBuilder.query(query);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = searchResponse.getHits();
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);
List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
}
布尔查询
boolQuery:对多个查询条件连接。连接方式
- must(and):条件必须成立
- must_not(not):条件必须不成立
- should(or):条件可以成立
- filter:条件必须成立,性能比must高。不会计算得分
脚本
# boolquery -must
GET goods/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"brandName": {
"value": "三星"
}
}
}
],
"filter": {
"term": {
"title": "电视"
}
}
}
}
}
# boolquery -filter
GET goods/_search
{
"query": {
"bool": {
"filter": {
"term": {
"brandName": "华为"
}
}
}
}
}
JavaAPI
/**
* 布尔查询:boolQuery
*/
@Test
public void testBoolQueryQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//1.构建boolQuery
BoolQueryBuilder query = QueryBuilders.boolQuery();
//2.构建各个查询条件
//2.1 查询品牌名称为:华为
TermQueryBuilder termQuery = QueryBuilders.termQuery("brandName", "华为");
query.must(termQuery);
//2.2查询题目包含手机
MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("title", "手机");
query.filter(matchQuery);
//2.3查询价格在2000-3000
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
rangeQuery.gte(2000);
rangeQuery.lte(3000);
query.filter(rangeQuery);
//3.使用boolQuery俩姐
sourceBuilder.query(query);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = searchResponse.getHits();
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);
List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
}
聚合查询
- 指标聚合:相当于MySQL的聚合函数。max、min、avg、sum等
脚本
# 聚合查询
# 指标聚合 聚合函数
GET goods/_search
{
"query": {
"match": {
"title": "手机"
}
},
"aggs": {
"max_price": {
"max": {
"field": "price"
}
}
}
}
- 桶聚合:相当于MySQL的 group by 操作。不要对text类型的数据进行分组,会失败
脚本
# 桶聚合 分组
GET goods/_search
{
"query": {
"match": {
"title": "电视"
}
},
"aggs": {
"goods_brands": {
"terms": {
"field": "brandName",
"size": 100
}
}
}
}
JavaAPI
/**
* 聚合查询:桶聚合,分组查询
* 1. 查询title包含手机的数据
* 2. 查询品牌列表
*/
@Test
public void testAggQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBulider = new SearchSourceBuilder();
// 1. 查询title包含手机的数据
MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机");
sourceBulider.query(query);
// 2. 查询品牌列表
/*
参数:
1. 自定义的名称,将来用于获取数据
2. 分组的字段
*/
AggregationBuilder agg = AggregationBuilders.terms("goods_brands").field("brandName").size(100);
sourceBulider.aggregation(agg);
searchRequest.source(sourceBulider);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = searchResponse.getHits();
//获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:"+value);
List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
//转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
// 获取聚合结果
Aggregations aggregations = searchResponse.getAggregations();
Map<String, Aggregation> aggregationMap = aggregations.asMap();
//System.out.println(aggregationMap);
Terms goods_brands = (Terms) aggregationMap.get("goods_brands");
List<? extends Terms.Bucket> buckets = goods_brands.getBuckets();
List brands = new ArrayList();
for (Terms.Bucket bucket : buckets) {
Object key = bucket.getKey();
brands.add(key);
}
for (Object brand : brands) {
System.out.println(brand);
}
}
高亮查询
- 高亮三要素:
- 高亮字段
- 前缀
- 后缀
脚本
GET goods/_search
{
"query": {
"match": {
"title": "电视"
}
},
"highlight": {
"fields": {
"title": {
"pre_tags" : "<font color='red'>",
"post_tags": "</font>"
}
}
}
}
JavaAPI
/**
*
* 高亮查询:
* 1. 设置高亮
* * 高亮字段
* * 前缀
* * 后缀
* 2. 将高亮了的字段数据,替换原有数据
*/
@Test
public void testHighLightQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBulider = new SearchSourceBuilder();
// 1. 查询title包含手机的数据
MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机");
sourceBulider.query(query);
//设置高亮
HighlightBuilder highlighter = new HighlightBuilder();
//设置三要素
highlighter.field("title");
highlighter.preTags("<font color='red'>");
highlighter.postTags("</font>");
sourceBulider.highlighter(highlighter);
// 2. 查询品牌列表
/*
参数:
1. 自定义的名称,将来用于获取数据
2. 分组的字段
*/
AggregationBuilder agg = AggregationBuilders.terms("goods_brands").field("brandName").size(100);
sourceBulider.aggregation(agg);
searchRequest.source(sourceBulider);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = searchResponse.getHits();
//获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:"+value);
List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
//转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
// 获取高亮结果,替换goods中的title
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField HighlightField = highlightFields.get("title");
Text[] fragments = HighlightField.fragments();
//替换
goods.setTitle(fragments[0].toString());
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
// 获取聚合结果
Aggregations aggregations = searchResponse.getAggregations();
Map<String, Aggregation> aggregationMap = aggregations.asMap();
//System.out.println(aggregationMap);
Terms goods_brands = (Terms) aggregationMap.get("goods_brands");
List<? extends Terms.Bucket> buckets = goods_brands.getBuckets();
List brands = new ArrayList();
for (Terms.Bucket bucket : buckets) {
Object key = bucket.getKey();
brands.add(key);
}
for (Object brand : brands) {
System.out.println(brand);
}
}
重建索引
- 随着业务需求的变更,索引的结构可能发生改变
- ElasticSearch的索引一旦创建,只允许添加字段,不允许改变字段。因为改变字段,需要重建倒排索引,影响内部缓存结构,性能太低
- 那么此时,就需要重建一个新的索引,并将原有索引的数据导入到新索引中
- 步骤
- 先删除老索引名称
- 新建索引名称,去一个别名为老元素的名臣
POST 新索引/_alias/老索引

本文深入介绍了Elasticsearch的核心概念,包括倒排索引、数据存储、搜索原理以及各种查询操作。详细讲解了如何创建、查询、删除索引,以及设置映射。还探讨了Elasticsearch的分词器,如内置分词器和IK分词器的使用。此外,展示了如何使用Java API进行文档的增删改查和批量操作,以及如何进行全文搜索、范围查询、聚合查询和高亮查询。最后,提到了Elasticsearch在SpringBoot中的整合应用。
8208





