一、引言


1.1 海量数据

在海量数据中执行搜索功能时,如果使用MySQL,效率太低。

1.2 全文检索

在海量数据中执行搜索功能时,如果使用MySQL,效率太低。

1.3 高亮显示

将搜索关键字,以红色的字体展示。

二、ES概述


2.1 ES的介绍

  • ES是一个使用Java语言并且基于Lucene编写的搜索引擎框架,他提供了分布式的全文搜索功能,提供了一个统一的基于RESTful风格的WEB接口,官方客户端也对多种语言都提供了相应的API。
  • Lucene:Lucene本身就是一个搜索引擎的底层。
  • 分布式:ES主要是为了突出他的横向扩展能力。
  • 全文检索:将一段词语进行分词,并且将分出的单个词语统一的放到一个分词库中,在搜索时,根据关键字去分词库中检索,找到匹配的内容。(倒排索引)
  • RESTful风格的WEB接口:操作ES很简单,只需要发送一个HTTP请求,并且根据请求方式的不同,携带参数的同,执行相应的功能。
  • 应用广泛:Github.com,WIKI,Gold Man用ES每天维护将近10TB的数据。

2.2 ES的由来

ES回忆时光

如何将es查询出来的结果转换为Object写入到JSON文件中 Java_java

2.3 ES和Solr

  • Solr在查询死数据时,速度相对ES更快一些。但是数据如果是实时改变的,Solr的查询速度会降低很多,ES的查询的效率基本没有变化。
  • Solr搭建基于需要依赖Zookeeper来帮助管理。ES本身就支持集群的搭建,不需要第三方的介入。
  • 最开始Solr的社区可以说是非常火爆,针对国内的文档并不是很多。在ES出现之后,ES的社区火爆程度直线上升,ES的文档非常健全。
  • ES对现在云计算和大数据支持的特别好。

2.4 倒排索引

将存放的数据,以一定的方式进行分词,并且将分词的内容存放到一个单独的分词库中。

当用户去查询数据时,会将用户的查询关键字进行分词。

然后去分词库中匹配内容,最终得到数据的id标识。

根据id标识去存放数据的位置拉取到指定的数据。

倒排索引

如何将es查询出来的结果转换为Object写入到JSON文件中 Java_es_02

三、 ElasticSearch安装


3.1 安装ES&Kibana

前提条件

1.如果防火墙开启则关闭,并且重启docker服务

2.将虚拟内存保持4g以上

3.设置sysctl参数,检查sysctl参数若没有则设置

echo "vm.max_map_count=655360">>/etc/sysctl.conf
sysctl -p  #执行当前命令

yml文件

version: "3.1"
services:
  elasticsearch:
    image: daocloud.io/library/elasticsearch:6.5.4
    restart: always
    container_name: elasticsearch
    ports:
      - 9200:9200
      - 9300:9300
  kibana:
    image: daocloud.io/library/kibana:6.5.4
    restart: always
    container_name: kibana
    ports:
      - 5601:5601
    environment:
      - elasticsearch_url=http://192.168.199.109:9200
    depends_on:
      - elasticsearch

解决kiban 无法访问问题

1.进入kibana,修改config/kibana.yml

docker exec -it kibana /bin/bash
kibana.example.org.crt  kibana.example.org.key  kibana.yml
bash-4.2$ vi config/kibana.yml

如何将es查询出来的结果转换为Object写入到JSON文件中 Java_elasticsearch_03

2.退出重启kibana

docker restart kibana

说明

9200用于外部通讯,基于http协议,程序与es的通信使用9200端口。
9300jar之间就是通过tcp协议通信,遵循tcp协议,es集群中的节点之间也通过9300端口进行通信。

3.2 安装IK分词器

PUT book_v6

POST /book_v6/_analyze
{
  "analyzer": "ik_max_word",
  "text": "我是中国人"
}

四、 ElasticSearch基本操作


4.1 ES的结构

4.1.1 索引Index,分片和备份

  • ES的服务中,可以创建多个索引。
  • 每一个索引默认被分成5片存储。
  • 每一个分片都会存在至少一个备份分片。
  • 备份分片默认不会帮助检索数据,当ES检索压力特别大的时候,备份分片才会帮助检索数据。
  • 备份的分片必须放在不同的服务器中。

索引分片备份

如何将es查询出来的结果转换为Object写入到JSON文件中 Java_es_04

4.1.2 类型 Type

一个索引下,可以创建多个类型。

Ps:根据版本不同,类型的创建也不同。

类型

如何将es查询出来的结果转换为Object写入到JSON文件中 Java_elasticsearch_05

4.1.3 文档 Doc

一个类型下,可以有多个文档。这个文档就类似于MySQL表中的多行数据。

文档

如何将es查询出来的结果转换为Object写入到JSON文件中 Java_java_06

4.1.4 属性 Field

一个文档中,可以包含多个属性。类似于MySQL表中的一行数据存在多个列。

属性

如何将es查询出来的结果转换为Object写入到JSON文件中 Java_elasticsearch_07

4.2 操作ES的RESTful语法

  • GET请求:
  • http://ip:port/index:查询索引信息
  • http://ip:port/index/type/doc_id:查询指定的文档信息
  • POST请求:
  • http://ip:port/index/type/_search:查询文档,可以在请求体中添加json字符串来代表查询条件
  • http://ip:port/index/type/doc_id/_update:修改文档,在请求体中指定json字符串代表修改的具体信息
  • PUT请求:
  • http://ip:port/index:创建一个索引,需要在请求体中指定索引的信息,类型,结构
  • http://ip:port/index/type/_mappings:代表创建索引时,指定索引文档存储的属性的信息
  • DELETE请求:
  • http://ip:port/index:删除索引
  • http://ip:port/index/type/doc_id:删除指定的文档

4.3 索引的操作

4.3.1 创建一个索引

语法如下

# 创建一个索引
PUT /person
{
  "settings": {
    "number_of_shards": 5,
    "number_of_replicas": 1
  }
}

4.3.2 查看索引信息

语法如下

# 查看索引信息
GET /person

查看信息

如何将es查询出来的结果转换为Object写入到JSON文件中 Java_java_08

4.3.3 删除索引

语法如下

# 删除索引
DELETE /person

4.4 ES中Field可以指定的类型

  • 字符串类型:
  • text:一把被用于全文检索。 将当前Field进行分词。
  • keyword:当前Field不会被分词。
  • 数值类型:
  • long:取值范围为-9223372036854774808~922337203685477480(-2的63次方到2的63次方-1),占用8个字节
  • integer:取值范围为-2147483648~2147483647(-2的31次方到2的31次方-1),占用4个字节
  • short:取值范围为-32768~32767(-2的15次方到2的15次方-1),占用2个字节
  • byte:取值范围为-128~127(-2的7次方到2的7次方-1),占用1个字节
  • double:1.797693e+308~ 4.9000000e-324 (e+308表示是乘以10的308次方,e-324表示乘以10的负324次方)占用8个字节
  • float:3.402823e+38 ~ 1.401298e-45(e+38表示是乘以10的38次方,e-45表示乘以10的负45次方),占用4个字节
  • half_float:精度比float小一半。
  • scaled_float:根据一个long和scaled来表达一个浮点型,long-345,scaled-100 -> 3.45
  • 时间类型:
  • date类型,针对时间类型指定具体的格式
  • 布尔类型:
  • boolean类型,表达true和false
  • 二进制类型:
  • binary类型暂时支持Base64 encode string
  • 范围类型:
  • long_range:赋值时,无需指定具体的内容,只需要存储一个范围即可,指定gt,lt,gte,lte
  • integer_range:同上
  • double_range:同上
  • float_range:同上
  • date_range:同上
  • ip_range:同上
  • 经纬度类型:
  • geo_point:用来存储经纬度的
  • ip类型:
  • ip:可以存储IPV4或者IPV6

其他的数据类型参考官网:Field datatypes | Elasticsearch Guide [6.5] | Elastic

4.5 创建索引并指定数据结构

语法如下

# 创建索引,指定数据结构
PUT /book
{
  "settings": {
    # 分片数
    "number_of_shards": 5,
    # 备份数
    "number_of_replicas": 1
  },
  # 指定数据结构
  "mappings": {
    # 类型 Type
    "novel": {
      # 文档存储的Field
      "properties": {
        # Field属性名
        "name": {
            # 类型
          "type": "text",
            # 指定分词器
          "analyzer": "ik_max_word",
            # 指定当前Field可以被作为查询的条件
          "index": true ,
            # 是否需要额外存储
          "store": false 
        },
        "author": {
          "type": "keyword"
        },
        "count": {
          "type": "long"
        },
        "on_sale": {
          "type": "date",
           # 时间类型的格式化方式 
          "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
        },
        "descr": {
          "type": "text",
          "analyzer": "ik_max_word"
        }
      }
    }
  }
}

4.6 文档的操作

文档在ES服务中的唯一标识,_index_type_id三个内容为组合,锁定一个文档,操作是添加还是修改。

4.6.1 新建文档

自动生成_id

# 添加文档,自动生成id
POST /book/novel
{
  "name": "盘龙",
  "author": "我吃西红柿",
  "count": 100000,
  "on_sale": "2000-01-01",
  "descr": "山重水复疑无路,柳暗花明又一村"
}

手动指定_id

# 添加文档,手动指定id
PUT /book/novel/1
{
  "name": "红楼梦",
  "author": "曹雪芹",
  "count": 10000000,
  "on_sale": "1985-01-01",
  "descr": "一个是阆苑仙葩,一个是美玉无瑕"
}

4.6.2 修改文档

覆盖式修改

# 添加文档,手动指定id
PUT /book/novel/1
{
  "name": "红楼梦",
  "author": "曹雪芹",
  "count": 4353453,
  "on_sale": "1985-01-01",
  "descr": "一个是阆苑仙葩,一个是美玉无瑕"
}

doc修改方式

修改时只修改部分内容,不会修改版本

# 修改文档,基于doc方式
POST /book/novel/1/_update
{
  "doc": {
     # 指定上需要修改的field和对应的值
    "count": "1234565"
  }
}

4.6.3 删除文档

根据id删除

# 根据id删除文档
DELETE /book/novel/_id

查看索引结构

GET /book7
{
  "book7" : {
    "aliases" : { },
    "mappings" : {
      "novel" : {
        "properties" : {
          "author" : {
            "type" : "keyword"
          },
          "count" : {
            "type" : "integer"
          },
          "descr" : {
            "type" : "text",
            "store" : true,
            "analyzer" : "ik_max_word",
            "search_analyzer" : "ik_smart"
          },
          "id" : {
            "type" : "text",
            "fields" : {   #不但可以文本,还可以是关键字
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          },
          "name" : {
            "type" : "text",
            "fielddata" : true
          },
          "on_sale" : {
            "type" : "date",
            "format" : "year_month_day"
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "refresh_interval" : "1s",
        "number_of_shards" : "5",
        "provided_name" : "book7",
        "creation_date" : "1615809289668",
        "store" : {
          "type" : "fs"
        },
        "number_of_replicas" : "1",
        "uuid" : "UtBQle9ATNu6J_HqPBkepQ",
        "version" : {
          "created" : "6050499"
        }
      }
    }
  }
}

elasticsearch入门使用(二) Mapping + field type字段类型 - nickchou - 博客园

五、Java操作ElasticSearch【重点


5.1 Springboot连接ES

创建Maven工程

导入依赖




<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/>

    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
    </dependencies>




创建配置文件application.properties

# ELASTICSEARCH (ElasticsearchProperties)
# Elasticsearch cluster name. 必须写正确
spring.data.elasticsearch.cluster-name=docker-cluster
# Comma-separated list of cluster node addresses. 必须是 9300
spring.data.elasticsearch.cluster-nodes=192.168.12.130:9300
# Whether to enable Elasticsearch repositories.
spring.data.elasticsearch.repositories.enabled=true

server.port=8888




创建启动类

@SpringBootApplication
public class MyApplication {


    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class,args);
    }

}




创建实体类

/**
 *  "name": "红楼梦",
 *   "author": "曹雪芹1",
 *   "count": 10000000,
 *   "on_sale": "1985-01-01",
 *   "descr": "一个是阆苑仙葩,一个是美玉无瑕"
 */
@Document(indexName = "book7",replicas = 1,shards = 5,type = "novel")
public class Book {


    @Id
    private String id;

    // fielddata 用于sort
    @Field(type = FieldType.Text,fielddata = true)
    private String name;

    @Field(type = FieldType.Keyword)
    private  String author;

    @Field(type = FieldType.Integer)
    private int count;

    @Field(type = FieldType.Date,format = DateFormat.year_month_day)
    private String on_sale;

    @Field(analyzer = "ik_max_word", searchAnalyzer = "ik_smart", type = FieldType.Text, store = true)
    private String descr;

。。。。
}

5.2 Java操作索引

5.2.1 创建索引

代码如下

@SpringBootTest
@RunWith(SpringRunner.class)
public class MyApplicationTest {


    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    
     @Test
    public void  createIndex(){
        
        // 创建索引
        elasticsearchTemplate.createIndex(Book.class);

        // 设置mapping
        elasticsearchTemplate.putMapping(Book.class);
    }
}

5.2.2 增加/覆盖数据

代码如下

@Test
    public void addBook(){

        Book book = new Book();

        book.setAuthor("张三");
        book.setCount(1000555);
        book.setDescr("一个是阆苑仙葩,一个是美玉无瑕");
        book.setId("2");
        book.setName("盘龙");
        book.setOn_sale("2020-01-12");

        IndexQuery indexQuery = new IndexQueryBuilder()
                .withId(book.getId())
                .withObject(book)
                .build();
//      String id =   elasticsearchTemplate.index(indexQuery);

//        System.out.println("id:"+id);

        List<IndexQuery> queries = new ArrayList<>();
        queries.add(indexQuery);
        elasticsearchTemplate.bulkIndex(queries);
    }

5.2.3 更新数据

代码如下

/**
     * 更新数据
     */
    @Test
    public void  updateBook(){

        Map<String, Object> params = new HashMap<>();
        // 其中某一个属性
        params.put("count", 999);
        
        UpdateRequest updateRequest = new UpdateRequest();
        updateRequest.doc(params);

        UpdateQueryBuilder updateQueryBuilder = new UpdateQueryBuilder();
        updateQueryBuilder.withId("1");

        updateQueryBuilder.withUpdateRequest(updateRequest);
        updateQueryBuilder.withClass(Book .class);

        UpdateQuery build = updateQueryBuilder.build();
        UpdateResponse update1 = elasticsearchTemplate.update(build);

        System.out.println(update1.getResult().getLowercase());



    }




5.2.3 删除数据

/**
     * 删除
     */
    @Test
    public void delete(){
        // 根据id删除
//        elasticsearchTemplate.delete(Book.class, "2");

        // 删除索引
        elasticsearchTemplate.deleteIndex(Book.class);
    }

5.2.4 查选

/**
     * 根据id 查选
     */
    @Test
    public void queryById(){

        GetQuery query = new GetQuery();
        query.setId("1");
        Book book = elasticsearchTemplate.queryForObject(query, Book.class);

        System.out.println("book:"+book);
    }

    /**
     * 模糊查询
     */
    @Test
    public void query(){

        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.queryStringQuery("descr:美玉"))
                .withPageable(new PageRequest(0, 20))
                .build();
        List<Book> list = elasticsearchTemplate.queryForList(searchQuery, Book.class);

       System.out.println("list:"+list);
    }

六、 ElasticSearch的各种查询


1.下载数据

webget https://raw.githubusercontent.com/elastic/elasticsearch/master/docs/src/test/resources/accounts.json

2.加载 json数据 注意es连接地址

curl -H "Content-Type: application/json" -XPOST "192.168.13.100:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json"




3.数据分析

{
    "account_number": 1,  #账号
    "balance": 39225,     #账户余额
    "firstname": "Amber",  #名子
    "lastname": "Duke",    #姓氏
    "age": 32,             #年龄
    "gender": "M",         #性别 
    "address": "880 Holmes Lane",  #地址
    "employer": "Pyrami",          #雇佣者  老板 公司  
    "email": "amberduke@pyrami.com", # 邮箱
    "city": "Brogan",                #所在城市  
    "state": "IL"                    #国家简称 
}

6.1 term&terms查询【重点

6.1.1 term查询

term的查询是代表完全匹配,搜索之前不会对你搜索的关键字进行分词,对你的关键字去文档分词库中去匹配内容。


它被用作精确查询,比如数字,时间,布尔,和字段属性为keyword类型的关键字,后面讲的聚合也只支持精确字段,而分词字段text是不被支持的。

字符串只能匹配keyword

# term查询

POST /bank/_doc/_search
{
  "from": 0,    
  "size": 5,      
  "query": {
    "term": {
      "state.keyword": {
        "value": "DE"
      }
    }
  }
}

6.1.2 terms查询

terms和term的查询机制是一样,都不会将指定的查询关键字进行分词,直接去分词库中匹配,找到相应文档内容。

terms是在针对一个字段包含多个值的时候使用。

term:where province = 北京;

terms:where province = 北京 or province = ?or province = ?

# terms查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "terms": {
      "province": [
        "北京",
        "山西",
        "武汉"
      ]
    }
  }
}

POST /bank/_doc/_search
{
  "from": 0,
  "size": 20
  , "query": {
    "terms": {
      "state.keyword": [
        "DE",
        "PA"
      ]
    }
  }
  
}

6.2 match查询【重点

match查询属于高层查询,他会根据你查询的字段类型不一样,采用不同的查询方式。

  • 查询的是日期或者是数值的话,他会将你基于的字符串查询内容转换为日期或者数值对待。
  • 如果查询的内容是一个不能被分词的内容(keyword),match查询不会对你指定的查询关键字进行分词。
  • 如果查询的内容时一个可以被分词的内容(text),match会将你指定的查询内容根据一定的方式去分词,去分词库中匹配指定的内容。

match查询,实际底层就是多个term查询,将多个term查询的结果给你封装到了一起。

6.2.1 match_all查询

查询全部内容,不指定任何查询条件。

# match_all查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match_all": {}
  }
}


POST /bank/_doc/_search
{
    "from": 0,
  "size": 2,
  "query": {
    "match_all": {}
  }
}

6.2.2 match查询

指定一个Field作为筛选的条件

# match查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match": {
      "smsContent": "收货安装"
    }
  }
}

#只要含有关键词 就查询出来
POST /bank/_doc/_search
{
  "from":0,
  "size":20,
  "query":{
    "match":{
      "address":"Avenue Baycliff"
    }
  }
}

6.2.3 布尔match查询

基于一个Field匹配的内容,采用and或者or的方式连接

# 布尔match查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match": {
      "smsContent": {
        "query": "中国 健康",
        "operator": "and"      # 内容既包含中国也包含健康
      }
    }
  }
}


# 布尔match查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match": {
      "smsContent": {
        "query": "中国 健康",
        "operator": "or"        # 内容包括健康或者包括中国
      }
    }
  }
}

#或者关系
POST /bank/_doc/_search
{
  "from":0,
  "size":20,
  "query":{
    "match":{
      "address":{
        "query":"Avenue Baycliff",
        "operator":"or"
      }
     
    }
  }
}

6.2.4 multi_match查询

match针对一个field做检索,multi_match针对多个field进行检索,多个field对应一个text。

# multi_match 查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "multi_match": {
      "query": "北京",                    # 指定text
      "fields": ["province","smsContent"]    # 指定field们
    }
  }
}
# 两个字段含有又给就可以
POST /bank/_doc/_search
{
  "from":0,
  "size":20,
  "query":{
     "multi_match": {
      "query": "Hondah",                
      "fields": ["city","address"]    
    }
  }
}

6.3 其他查询

6.3.1 id查询

根据id查询 where id = ?

# id查询
GET /sms-logs-index/sms-logs-type/1


GET /bank/_doc/1

6.3.2 ids查询

根据多个id查询,类似MySQL中的where id in(id1,id2,id2...)

# ids查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "ids": {
      "values": ["1","2","3"]
    }
  }
}



POST /bank/_doc/_search
{
  "query":{
    "ids":{
      "values":["1","2","3"]
    }
  }
}

6.3.5 wildcard查询

通配查询,和MySQL中的like是一个套路,可以在查询时,在字符串中指定通配符*和占位符?

只能匹配部分字,不可以匹配一个词

# 卡方搜索
# 注意是搜索词是关键词    关键词   *爱*
# 我们搜索的 文本 如果是 中文必须配置分词器 "analyzer" : "ik_max_word",否则默认的分词器 会将 文档中的属性切分为每一个字 无法切分

POST /book/novel/_search
{
   "from":0,
   "size":5,
    "query":{
       "wildcard":{
         "decr":{
           "value":"*爱情*"
         }
       }
    }
}


#也可 去查询keyword 此时会将 目标属性作为整体去查询
POST /book/novel/_search
{
   "from":0,
   "size":5,
    "query":{
       "wildcard":{
         "author":{
           "value":"*雪*"
         }
       }
    }
}


GET /book

6.3.6 range查询

范围查询,只针对数值类型,对某一个Field进行大于或者小于的范围指定

# range 查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "range": {
      "fee": {
        "gt": 5,
        "lte": 10
         # 可以使用 gt:>      gte:>=     lt:<     lte:<=
      }
    }
  }
}

POST /bank/_doc/_search
{
  "query": {
    "range": {
      "age": {
        "gt": 24,
        "lte": 100
       
      }
    }
  }
}

6.6 复合查询

6.6.1 bool查询

复合过滤器,将你的多个查询条件,以一定的逻辑组合在一起。

  • must: 所有的条件,用must组合在一起,表示And的意思
  • must_not:将must_not中的条件,全部都不能匹配,标识Not的意思
  • should:所有的条件,用should组合在一起,表示Or的意思
# 查询省份为武汉或者北京
# 运营商不是联通
# smsContent中包含中国和平安
# bool查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "province": {
              "value": "北京"
            }
          }
        },
        {
          "term": {
            "province": {
              "value": "武汉"
            }
          }
        }
      ],
      "must_not": [
        {
          "term": {
            "operatorId": {
              "value": "2"
            }
          }
        }
      ],
      "must": [
        {
          "match": {
            "smsContent": "中国"
          }
        },
        {
          "match": {
            "smsContent": "平安"
          }
        }
      ]
    }
  }
}

POST /bank/_doc/_search
{
  "query": {
    "bool": {
      "should": [  #shoud 与 must 并存时不起作用
        {
          "term": {
            "city": {
              "value": "Yardville"
            }
          }
        },
        {
          "term": {
            "city": {
              "value": "Shaft"
            }
          }
        }
      ],
      "must_not": [
        {
          "term": {
            "age": {
              "value": "39"
            }
          }
        }
      ],
      "must": [
        {
          "match": {
            "state": "DE"
          }
        },
        {
          "match": {
            "lastname": "Bartlett"
          }
        }
      ]
    }
  }
}

注意:

must 与should 是否可以联合使用?

a.默认情况下must和should 不能混用,若使用should不生效

b.若要强行使用,则需要在should配置minimum_should_match =1

# must should 并存时 minimum_should_match必须配置为1
GET /bank/_search
{
  "size": 30, 
  "query":{
    "bool":{
      "must":{
          "match":{"gender":"M"}
      },
      "should":[
       
         {"term":{"age": 30}}
         
        ],
        "minimum_should_match": 1
    }
  }
}

6.7 filter查询

query,根据你的查询条件,去计算文档的匹配度得到一个分数,并且根据分数进行排序,不会做缓存的。

filter,根据你的查询条件去查询文档,不去计算分数,而且filter会对经常被过滤的数据进行缓存。

# filter查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "corpName": "盒马鲜生"
          }
        },
        {
          "range": {
            "fee": {
              "lte": 4
            }
          }
        }
      ]
    }
  }
}


POST /bank/_doc/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "match": {
            "address": "731"
          }
        },
        {
          "range": {
            "age": {
              "lte": 40
            }
          }
        }
      ]
    }
  }
}

6.8 高亮查询【重点

高亮查询就是你用户输入的关键字,以一定的特殊样式展示给用户,让用户知道为什么这个结果被检索出来。

高亮展示的数据,本身就是文档中的一个Field,单独将Field以highlight的形式返回给你。

ES提供了一个highlight属性,和query同级别的。

  • fragment_size:指定高亮数据展示多少个字符回来。
  • pre_tags:指定前缀标签,举个栗子< font color="red" >
  • post_tags:指定后缀标签,举个栗子< /font >
  • fields:指定哪几个Field以高亮形式返回

效果图

如何将es查询出来的结果转换为Object写入到JSON文件中 Java_数据_09

 

RESTful实现

# highlight查询
POST /sms-logs-index/sms-logs-type/_search
{
  "query": {
    "match": {
      "smsContent": "盒马"
    }
  },
  "highlight": {
    "fields": {
      "smsContent": {}
    },
    "pre_tags": "<font color='red'>",
    "post_tags": "</font>",
    "fragment_size": 10
  }
}


POST /bank/_doc/_search
{
  "query": {
    "match": {
      "address": "Street"
    }
  },
  "highlight": {
    "fields": {
      "address": {}
    },
    "pre_tags": "<font color='red'>",
    "post_tags": "</font>",
    "fragment_size": 10
  }
}

6.9 聚合查询【重点

ES的聚合查询和MySQL的聚合查询类似,ES的聚合查询相比MySQL要强大的多,ES提供的统计数据的方式多种多样。

聚合操作,我们可以对数据进行分组的求和,求数,最大值,最小值,或者其它的自定义的统计功能,es对聚合有着不错的支持,需要注意的是,在对某字段进行聚合之后,需要开启这个字段的fielddata我,

匹配:开启fielddata 字段 或者 keyword

# ES聚合查询的RESTful语法
POST /index/type/_search
{
    "aggs": {
        "名字(agg)": {
            "agg_type": {
                "属性": "值"
            }
        }
    }
}

6.9.1 去重计数查询

去重计数,即Cardinality,第一步先将返回的文档中的一个指定的field进行去重,统计一共有多少条

field 对应的列必须是关键字,如果不是需要province.keyword

# 去重计数查询 北京 上海 武汉 山西
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "agg": {
      "cardinality": {
        "field": "province"
      }
    }
  }
}

POST /bank/_doc/_search
{
  "aggs": {
    "agg": {
      "cardinality": {
        "field": "state.keyword"
      }
    }
  }
}

6.9.2 范围统计

统计一定范围内出现的文档个数,比如,针对某一个Field的值在 0~100,100~200,200~300之间文档出现的个数分别是多少。

范围统计可以针对普通的数值,针对时间类型,针对ip类型都可以做相应的统计。

range,date_range,ip_range

数值统计

# 数值方式范围统计
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "agg": {
      "range": {
        "field": "fee",
        "ranges": [
          {
            "to": 5
          },
          {
            "from": 5,    # from有包含当前值的意思  
            "to": 10
          },
          {
            "from": 10
          }
        ]
      }
    }
  }
}

POST /bank/_doc/_search
{
  "aggs": {
    "agg": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "to": 15
          },
          {
            "from": 15,  
            "to": 30
          },
          {
            "from": 50
          }
        ]
      }
    }
  }
}

时间范围统计

# 时间方式范围统计
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "agg": {
      "date_range": {
        "field": "createDate",
        "format": "yyyy", 
        "ranges": [
          {
            "to": 2000
          },
          {
            "from": 2000
          }
        ]
      }
    }
  }
}

6.9.3 统计聚合查询

他可以帮你查询指定Field的最大值,最小值,平均值,平方和等

使用:extended_stats

# 统计聚合查询
POST /sms-logs-index/sms-logs-type/_search
{
  "aggs": {
    "agg": {
      "extended_stats": {
        "field": "fee"
      }
    }
  }
}


#平均年龄

POST /bank/_doc/_search
{
  "aggs": {
    "agg": {
      "extended_stats": {
        "field": "age"
      }
    }
  }
}




其他的聚合查询方式查看官方文档:Elasticsearch Guide [6.5] | Elastic

总结:

fielddata默认为false,使用场景为 文本的 聚合,排序或在脚本中使用,但是会占用大量的堆内存,一般来说可以使用.keyword 来替代使用


6.10 java查询

查询公式

NativeSearchQueryBuilder.withQuery(QueryBuilder1).withFilter(QueryBuilder2).withSort(SortBuilder1).withXXXX().build();

/**
     * 根据具属性名排序
     */
    @Test  // 报错  对应得列必须配置     @Field(type = FieldType.Text,fielddata = true)
    public void sort(){
        Pageable pageable= new PageRequest(0, 20);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.queryStringQuery("id:2"))
                .withSort(SortBuilders.fieldSort("name"))
                .withPageable(pageable)
                .build();
        Page<Book> list = elasticsearchTemplate.queryForPage(searchQuery, Book.class);

        System.out.println("list:"+list);
    }



    /**
     * 模糊查询
     *
     * 此模糊查询与mysql中的模糊查询不太一样,此模糊查询类似分词匹配。
     * 比如有两条数据:1、我今天非常高兴 2、他摔倒很高兴
     * 输入:今天高兴
     * 这两条数据都能匹配上。
     *
     */
    @Test
    public void search(){

        Pageable pageable = new PageRequest(0, 10);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("descr", "无路美玉"))
                .withPageable(pageable)
                .build();
        List<Book> list = elasticsearchTemplate.queryForList(searchQuery, Book.class);

        System.out.println("list:"+list);
    }

    /**
     *其余匹配类似mysql中like "%word%"的模糊匹配
     */
    @Test
    public void like(){

        Pageable pageable = new PageRequest(0, 10);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchPhraseQuery("descr", "美玉"))
                .withPageable(pageable)
                .build();

        List<Book> list = elasticsearchTemplate.queryForList(searchQuery, Book.class);

        System.out.println("list:"+list);
    }

    /**
     * 全匹配 查询不到 只有类型 是关键字时 才可以匹配  test 无法匹配
     */
    @Test
    public void term(){
        Pageable pageable = new PageRequest(0, 10);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
//                .withQuery(QueryBuilders.termQuery("author", "张三"))
                .withQuery(QueryBuilders.termQuery("name", "盘龙"))  // 非关键字无法查询
                .withPageable(pageable)
                .build();


        List<Book> list = elasticsearchTemplate.queryForList(searchQuery, Book.class);
        System.out.println("list:"+list);
    }

    /**
     * 即boolQuery,可以设置多个条件的查询方式。它的作用是用来组合多个Query,有四种方式来组合,must,mustnot,filter,should。
     * must代表返回的文档必须满足must子句的条件,会参与计算分值;
     * filter代表返回的文档必须满足filter子句的条件,但不会参与计算分值;
     * should代表返回的文档可能满足should子句的条件,也可能不满足,有多个should时满足任何一个就可以,通过minimum_should_match设置至少满足几个。
     * mustnot代表必须不满足子句的条件。
     */
    @Test
    public void boolQuery(){
        QueryBuilder filterQuery = QueryBuilders
                .boolQuery()
//                .filter(QueryBuilders.termQuery("name", "菜鸟"))
                .filter(QueryBuilders.matchPhraseQuery("descr", "美玉"));

        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(filterQuery).build();


        List<Book> list = elasticsearchTemplate.queryForList(searchQuery, Book.class);
        System.out.println("list:"+list);


    }

    /**
     * 高亮显示
     */
    @Test
    public void HeightLight(){
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //在tag,description域中搜索匹配的内容
        //HighlightFields表示高亮设置
        //new HighlightBuilder.Field("tag").preTags("<span style=\"color:red\">").postTags("</span>")表示设置高亮域为tag,并加上前置与后置标签
        NativeSearchQuery query =
                queryBuilder.withQuery(QueryBuilders.multiMatchQuery("美玉","name","descr"))
                        .withHighlightFields(new HighlightBuilder.Field("descr").preTags("<span style=\"color:red\">").postTags("</span>")).build();

        //通过elasticsearchTemplate并重写SearchResultMapper方法来实现高亮搜索
        AggregatedPage<Book> bookAggregatedPage = elasticsearchTemplate.queryForPage(query, Book.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                List<Book> books = new ArrayList<>();

                //得到匹配的所有选项
                SearchHits hits = searchResponse.getHits();

                for (SearchHit hit : hits) {
                    if (hits.getHits().length <= 0) {
                        return null;
                    }

                    Book book = new Book();

                    book.setId(hit.getId());
                    book.setName((String) hit.getSourceAsMap().get("name"));


                    //扩展性不强,使用反射更好
                    //防止标签不存在,描述存在的问题
                    if (hit.getHighlightFields().size()!=0) {
                        String highlightString = hit.getHighlightFields().get("descr").fragments()[0].toString();
                        book.setDescr(highlightString);
                    }
                    books.add(book);

                }
                return new AggregatedPageImpl<>((List<T>)books);
            }
        });

        bookAggregatedPage.getContent();

    }

    /**
     * 高亮显示
     */
    @Test
    public void HeightLight2(){
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //在tag,description域中搜索匹配的内容
        //HighlightFields表示高亮设置
        //new HighlightBuilder.Field("tag").preTags("<span style=\"color:red\">").postTags("</span>")表示设置高亮域为tag,并加上前置与后置标签
        NativeSearchQuery query =
                queryBuilder.withQuery(QueryBuilders.multiMatchQuery("美玉","name","descr"))
                        .withHighlightFields(new HighlightBuilder.Field("descr").preTags("<span style=\"color:red\">").postTags("</span>")).build();

        //通过elasticsearchTemplate并重写SearchResultMapper方法来实现高亮搜索
        AggregatedPage<Book> bookAggregatedPage = elasticsearchTemplate.queryForPage(query, Book.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                List<Book> books = new ArrayList<>();

                //得到匹配的所有选项
                SearchHits hits = searchResponse.getHits();

                for (SearchHit hit : hits) {
                    if (hits.getHits().length <= 0) {
                        return null;
                    }

                    Book book = new Book();

                    book.setId(hit.getId());
                    book.setName((String) hit.getSourceAsMap().get("name"));
//                    book.setDescr((String) hit.getSourceAsMap().get("name"));

                    setHighLight(hit,"descr",book);

                    books.add(book);

                }
                return new AggregatedPageImpl<>((List<T>)books);
            }
        });

        System.out.println("list:"+bookAggregatedPage.getContent());

    }


    public void setHighLight(SearchHit searchHit, String field, Object object) {
        Map<String, HighlightField> highlightFieldMap = searchHit.getHighlightFields();
        HighlightField highlightField = highlightFieldMap.get(field);
        if (highlightField != null) {
            String highLightMessage = highlightField.fragments()[0].toString();
            String capitalize = StringUtils.capitalize(field);
            String methodName = "set"+capitalize;
            Class<?> clazz = object.getClass();
            try {
                Method setMethod = clazz.getMethod(methodName, String.class);
                setMethod.invoke(object, highLightMessage);

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }


    /**
     * 聚合查询
     * 根据字段 author  聚合查选
     */
    @Test
    public void aggregation1(){
        // 创建一个查询条件对象
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 拼接查询条件
        queryBuilder.should(QueryBuilders.multiMatchQuery("美玉","name","descr"));
        // 创建聚合查询条件
        TermsAggregationBuilder agg = AggregationBuilders.terms("author_count").field("author");//keyword表示不使用分词进行聚合

        // 创建查询对象
        SearchQuery build = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder) //添加查询条件
                .addAggregation(agg) // 添加聚合条件
                .withPageable(PageRequest.of(0, 10)) //符合查询条件的文档分页(不是聚合的分页)
                .build();

        // 执行查询
        AggregatedPage<Book> testEntities = elasticsearchTemplate.queryForPage(build, Book.class);
        // 取出聚合结果
        Aggregations entitiesAggregations = testEntities.getAggregations();
        Terms terms = (Terms) entitiesAggregations.asMap().get("author_count");
        // 遍历取出聚合字段列的值,与对应的数量
        for (Terms.Bucket bucket : terms.getBuckets()) {
            String keyAsString = bucket.getKeyAsString(); // 聚合字段列的值
            long docCount = bucket.getDocCount();// 聚合字段对应的数量
            System.out.println("keyAsString={},value={}"+ keyAsString+ docCount);
        }
    }


    /**
     * 求平局数量
     */
    @Test
    public void aggregation2(){
        // 创建一个查询条件对象
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 拼接查询条件
        queryBuilder.should(QueryBuilders.multiMatchQuery("美玉","name","descr"));
        // 创建聚合查询条件
        AvgAggregationBuilder ab= AggregationBuilders.avg("avg_count").field("count");

        // 创建查询对象
        SearchQuery build = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder) //添加查询条件
                .addAggregation(ab) // 添加聚合条件
                .withPageable(PageRequest.of(0, 10)) //符合查询条件的文档分页(不是聚合的分页)
                .build();

        // 执行查询
        AggregatedPage<Book> testEntities = elasticsearchTemplate.queryForPage(build, Book.class);
        // 取出聚合结果
        Aggregations entitiesAggregations = testEntities.getAggregations();
        InternalAvg avg = (InternalAvg) entitiesAggregations.asMap().get("avg_count");

        System.out.println(avg.getValueAsString());

    }

聚合工具类

//(1)统计某个字段的数量
  ValueCountBuilder vcb=  AggregationBuilders.count("count_uid").field("uid");
//(2)去重统计某个字段的数量(有少量误差)
 CardinalityBuilder cb= AggregationBuilders.cardinality("distinct_count_uid").field("uid");
//(3)聚合过滤
FilterAggregationBuilder fab= AggregationBuilders.filter("uid_filter").filter(QueryBuilders.queryStringQuery("uid:001"));
//(4)按某个字段分组
TermsBuilder tb=  AggregationBuilders.terms("group_name").field("name");
//(5)求和
SumBuilder  sumBuilder=	AggregationBuilders.sum("sum_price").field("price");
//(6)求平均
AvgBuilder ab= AggregationBuilders.avg("avg_price").field("price");
//(7)求最大值
MaxBuilder mb= AggregationBuilders.max("max_price").field("price"); 
//(8)求最小值
MinBuilder min=	AggregationBuilders.min("min_price").field("price");
//(9)按日期间隔分组
DateHistogramBuilder dhb= AggregationBuilders.dateHistogram("dh").field("date");
//(10)获取聚合里面的结果
TopHitsBuilder thb=  AggregationBuilders.topHits("top_result");
//(11)嵌套的聚合
NestedBuilder nb= AggregationBuilders.nested("negsted_path").path("quests");
//(12)反转嵌套
AggregationBuilders.reverseNested("res_negsted").path("kps ");

参考:


聚合


/**
* java操作查询api  
* @author 231
*   参考文档:
*            
*            
*             https://www.jb51.net/article/166763.htm
*             http://www.mamicode.com/info-detail-2731123.html
*               *************重点
*             重点*****
*
*              ElasticSearchTemplate
*            
*            
*            
*            
*//*

相似度查询