分布式搜索(1)

分布式搜索(1)

Elasticsearch

一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能

概念

elastic stack(ELK): 是以elasticsearch为核心的技术栈,包括beats、Logstash、kibana、elasticsearch

正向索引:根据id索引来查找文档

倒排索引:根据搜索的词条找id再找文档

安装es

创建一个网络, 让es和kibana容器互联

1
docker network create es-net
  • 拉取镜像(体积比较大,下载时间可能比较长)

    1
    docker pull elasticsearch:7.12.1
  • 运行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    docker run -d \
    --name es \
    -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
    -e "discovery.type=single-node" \
    -v es-data:/usr/share/elasticsearch/data \
    -v es-plugins:/usr/share/elasticsearch/plugins \
    --privileged \
    --network es-net \
    -p 9200:9200 \
    -p 9300:9300 \
    elasticsearch:7.12.1

命令解释:

  • -e "cluster.name=es-docker-cluster":设置集群名称
  • -e "http.host=0.0.0.0":监听的地址,可以外网访问
  • -e "ES_JAVA_OPTS=-Xms512m -Xmx512m":内存大小
  • -e "discovery.type=single-node":非集群模式
  • -v es-data:/usr/share/elasticsearch/data:挂载逻辑卷,绑定es的数据目录
  • -v es-logs:/usr/share/elasticsearch/logs:挂载逻辑卷,绑定es的日志目录
  • -v es-plugins:/usr/share/elasticsearch/plugins:挂载逻辑卷,绑定es的插件目录
  • --privileged:授予逻辑卷访问权
  • --network es-net :加入一个名为es-net的网络中
  • -p 9200:9200:端口映射配置

安装kibana

kibana可以给我们提供一个elasticsearch的可视化界面

  • 拉取镜像(一定要和es版本一样)

    1
    docker pull kibana:7.12.1
  • 运行

    1
    2
    3
    4
    5
    6
    docker run -d \
    --name kibana \
    -e ELASTICSEARCH_HOSTS=http://es:9200 \
    --network=es-net \
    -p 5601:5601 \
    kibana:7.12.1

命令解释

  • --network es-net :加入一个名为es-net的网络中,与elasticsearch在同一个网络中
  • -e ELASTICSEARCH_HOSTS=http://es:9200":设置elasticsearch的地址,因为kibana已经与elasticsearch在一个网络,因此可以用容器名直接访问elasticsearch
  • -p 5601:5601:端口映射配置

安装ik

es内置的分词器不支持中文。ik分词器是一个标准的中文分词器

  • 在线安装(速度慢)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 进入容器内部
    docker exec -it elasticsearch /bin/bash

    # 在线下载并安装
    ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip

    #退出
    exit
    #重启容器
    docker restart elasticsearch
  • 离线安装

    1
    2
    3
    4
    5
    6
    7
    # 查看es插件目录挂载的数据卷
    docker volume inspect es-plugins

    # 将ik分词器上传到数据卷内

    # 重启容器
    docker restart es
  • 测试

    1
    2
    3
    4
    5
    GET /_analyze
    {
    "analyzer": "ik_max_word",
    "text": "上海自来水来自上海"
    }

IK分词器包含两种模式:

  • ik_smart:最少切分

  • ik_max_word:最细切分

扩展词词典

IK分词器提供了扩展词汇的功能

  • ik/config目录下的IKAnalyzer.cfg.xml配置文件可以添加扩展词典

    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
    <properties>
    <comment>IK Analyzer 扩展配置</comment>
    <!--用户可以在这里配置自己的扩展字典 *** 添加扩展词典-->
    <entry key="ext_dict">ext.dic</entry>
    </properties>
  • 创建ext_dict,在文件中添加词汇,并放到ik/config里

    1
    2
    3
    # ext_dict文件
    哈哈哈哈哈哈
    传智播客
  • 重启es

    1
    docker restart es

ES基本操作

索引库操作

类似mysql的表

mapping是对索引库中文档的约束

常见的mapping属性包括:

  • type:字段数据类型,常见的简单类型有:
    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
    • 数值:long、integer、short、byte、double、float、
    • 布尔:boolean
    • 日期:date
    • 对象:object
  • index:是否创建索引,默认为true
  • analyzer:使用哪种分词器
  • properties:该字段的子字段

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 新增一个索引库
PUT /test
{
"mappings": {
"properties": {
"info":{
"type": "text",
"analyzer": "ik_smart"
},
"email":{
"type":"keyword",
"index": false
},
"name":{
"properties": {
"firstName":{
"type": "keyword"
},
"lastName":{
"type":"keyword"
}
}
}
}
}
}

# 查询索引库
GEt /test

# 给索引库新增字段 (索引库一旦创建,无法修改mapping,只能新增)
PUT /test/_mapping
{
"properties":{
"age":{
"type":"integer"
}
}
}

# 删除索引库
DELETE /test

文档操作

类似mysql的数据

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 新增文档
POST test/_doc/1
{
"info": "asdasd",
"email": "123@qq.com",
"name": {
"firstName": "san",
"lastName": "zhang"
}
}

# 查询文档
GET /test/_doc/1

# 删除文档
DELETE /test/_doc/1

# 修改文档(全量修改: 等于DELETE + POST,文档不存在也会新增)
PUT test/_doc/1
{
"info": "asdasd",
"email": "123@qq.com",
"name": {
"firstName": "si",
"lastName": "li"
}
}

# 修改文档(增量修改: 只修改部分)
POST test/_update/1
{
"doc":{
"email": "456@qq.com"
}
}

RESTAPI

ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。官方文档地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html

Java Rest Client又包括两种:

  • Java Low Level Rest Client
  • Java High Level Rest Client

设计mapping映射要考虑的信息包括:

  • 字段名
  • 字段数据类型
  • 是否参与搜索
  • 是否需要分词
  • 如果分词,分词器是什么?

创建客户端

通过客户端的api操作es

  • 导入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <properties>
    <java.version>1.8</java.version>
    <!--版本要和es的版本一致-->
    <elasticsearch.version>7.12.1</elasticsearch.version>
    </properties>

    <!--RestHighLevelClient依赖-->
    <dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    </dependency>
  • 通过new的方式创建客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package cn.itcast.hotel;

    public class HotelIndexTest {

    private RestHighLevelClient client;

    @Test
    public void test() {
    System.out.println(client);
    }

    //初始化
    @BeforeEach
    void setup() {
    client = new RestHighLevelClient(RestClient.builder(
    HttpHost.create("http://192.168.229.128:9200")
    ));
    }

    //结束后销毁
    @AfterEach
    void tearDown() throws IOException {
    this.client.close();
    }
    }

创建索引库

  • 准备创建语句

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    package cn.itcast.hotel.constants;

    public class ESConstants {
    public static String MAPPING_TEMPLATE = "{\n" +
    " \"mappings\": {\n" +
    " \"properties\": {\n" +
    " \"id\": {\n" +
    " \"type\": \"keyword\"\n" +
    " },\n" +
    " \"name\":{\n" +
    " \"type\": \"text\",\n" +
    " \"analyzer\": \"ik_max_word\",\n" +
    " \"copy_to\": \"all\"\n" +
    " },\n" +
    " \"address\":{\n" +
    " \"type\": \"keyword\",\n" +
    " \"index\": false\n" +
    " },\n" +
    " \"price\":{\n" +
    " \"type\": \"integer\"\n" +
    " },\n" +
    " \"score\":{\n" +
    " \"type\": \"integer\"\n" +
    " },\n" +
    " \"brand\":{\n" +
    " \"type\": \"keyword\",\n" +
    " \"copy_to\": \"all\"\n" +
    " },\n" +
    " \"city\":{\n" +
    " \"type\": \"keyword\",\n" +
    " \"copy_to\": \"all\"\n" +
    " },\n" +
    " \"starName\":{\n" +
    " \"type\": \"keyword\"\n" +
    " },\n" +
    " \"business\":{\n" +
    " \"type\": \"keyword\"\n" +
    " },\n" +
    " \"location\":{\n" +
    " \"type\": \"geo_point\"\n" +
    " },\n" +
    " \"pic\":{\n" +
    " \"type\": \"keyword\",\n" +
    " \"index\": false\n" +
    " },\n" +
    " \"all\":{\n" +
    " \"type\": \"text\",\n" +
    " \"analyzer\": \"ik_max_word\"\n" +
    " }\n" +
    " }\n" +
    " }\n" +
    "}";
    }

  • 通过client发送创建索引库请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
    * 创建索引库
    */
    @Test
    public void createIndex() throws IOException {
    //创建request请求对象
    CreateIndexRequest createIndexRequest = new CreateIndexRequest("hotel");
    //设置请求参数
    createIndexRequest.source(MAPPING_TEMPLATE, XContentType.JSON);
    //返送请求
    client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
    }

删除索引库

  • 通过client发送删除引库请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /**
    * 删除索引库
    */
    @Test
    public void deleteIndex() throws IOException {
    //创建request请求对象
    DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("hotel");
    //发送请求
    client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
    }

查询索引库

  • 通过client发送查询引库请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * 查询索引库
    */
    @Test
    public void getIndex() throws IOException {
    //创建request请求对象
    GetIndexRequest getIndexRequest = new GetIndexRequest("hotel");
    //发送请求
    boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
    System.out.println(exists ? "索引库存在" : "索引库不存在");
    }

新增文档

  • 通过client发送新增文档请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    package cn.itcast.hotel;

    @SpringBootTest
    public class HotelDocumentTest {

    private RestHighLevelClient client;

    @Autowired
    private IHotelService iHotelService;

    /**
    * 新增文档
    * @throws IOException
    */
    @Test
    public void createDoc() throws IOException {
    //从数据库中获取hotel对象
    Hotel hotel = iHotelService.getById(61083L);
    //数据库中的对象属性和索引库中不一致,要转换一下
    HotelDoc hotelDoc = new HotelDoc(hotel);

    //创建request请求
    IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
    //设置请求参数
    request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
    //发送请求
    client.index(request, RequestOptions.DEFAULT);
    }


    @BeforeEach
    void setup() {
    client = new RestHighLevelClient(RestClient.builder(
    HttpHost.create("http://192.168.229.128:9200")
    ));
    }

    @AfterEach
    void tearDown() throws IOException {
    this.client.close();
    }
    }

查询文档

  • 通过client发送查询文档请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * 查询文档
    * @throws IOException
    */
    @Test
    public void GetDoc() throws IOException {
    //创建请求
    GetRequest request = new GetRequest("hotel", "61083");
    //发送请求,获得响应结果
    GetResponse response = client.get(request, RequestOptions.DEFAULT);

    //将响应结构转为对象
    String json = response.getSourceAsString();
    HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
    System.out.println(hotelDoc);
    }

修改文档

  • 通过client发送修改文档请求,全量修改和新增操作一样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    * 更新文档(局部修改)
    * @throws IOException
    */
    @Test
    public void UpdateDoc() throws IOException {
    //创建请求
    UpdateRequest request = new UpdateRequest("hotel", "61083");
    //设置请求参数
    request.doc(
    "price", "999"
    );
    //发送请求
    client.update(request, RequestOptions.DEFAULT);
    }

删除文档

  • 通过client发送删除文档请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * 删除文档
    * @throws IOException
    */
    @Test
    public void deleteDoc() throws IOException {
    //创建请求
    DeleteRequest request = new DeleteRequest("hotel", "61083");
    //发送请求
    client.delete(request, RequestOptions.DEFAULT);
    }

批量新增文档

  • 通过client发送批量新增文档请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    * 批量新增文档
    * @throws IOException
    */
    @Test
    public void bulkCreateDoc() throws IOException {
    //创建请求
    BulkRequest request = new BulkRequest();

    //查询数据库中所有hotel信息
    List<Hotel> hotels = iHotelService.list();
    //将hotel转换为hotelDoc,再封装到请求中
    for (Hotel hotel : hotels) {
    HotelDoc hotelDoc = new HotelDoc(hotel);
    request.add(new IndexRequest("hotel")
    .id(hotel.getId().toString())
    .source(JSON.toJSONString(hotelDoc),XContentType.JSON)
    );
    }
    //发送请求
    client.bulk(request, RequestOptions.DEFAULT);
    }
  • 在kibana中用GET /hotel/_search可查看所有文档

DSL查询语法

Domain Specific Language

常见查询类型:

  • 查询所有
  • 全文检索查询: 对搜索条件分词得到词条,然后通过词条来查询相关字段
  • 精准查询:不会对搜索条件分词
  • 地理查询:给定坐标来查询
  • 复合查询:将其它简单查询组合起来,实现更复杂的搜索逻辑

通用查询语法

1
2
3
4
5
6
7
8
GET /indexName/_search
{
  "query": {
    "查询类型": {
      "查询条件": "条件值"
    }
  }
}

查询所有

match_all

  • 实例

    1
    2
    3
    4
    5
    6
    7
    # 查询hotel索引库中所有信息
    GET /hotel/_search
    {
    "query": {
    "match_all":{}
    }
    }

全文检索查询

match: 单字段查询

multi_match: 多字段查询

  • match实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 对内容进行分词,然后查询"all"字段
    GET /hotel/_search
    {
    "query": {
    "match": {
    "all": "外滩 如家"
    }
    }
    }
  • multi_match实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 对内容进行分词,然后查询"brand", "name", "business"三个字段
    GET /hotel/_search
    {
    "query": {
    "multi_match": {
    "query": "外滩 如家",
    "fields": ["brand", "name", "business"]
    }
    }
    }

精准查询

term:根据词条精确值查询

range:根据值的范围查询

  • term实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 查询"city"字段为"上海"的信息
    GET /hotel/_search
    {
    "query": {
    "term": {
    "city": {
    "value": "上海"
    }
    }
    }
    }
  • range实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 查询"price"字段值为[200, 300]之间的信息
    # gte: greater then equals , lte: less then equals
    GET /hotel/_search
    {
    "query": {
    "range": {
    "price": {
    "gte": 200,
    "lte": 300
    }
    }
    }
    }

地理查询

geo_distance:指定坐标和半径来查询范围内的数据

geo_bounding_box:指定两个坐标,查询形成的矩形内的数据

  • geo_distance实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 以"31.21,121.5" 为点做半径为15km的圆
    GET /hotel/_search
    {
    "query": {
    "geo_distance":{
    "distance": "15km",
    "location": "31.21,121.5"
    }
    }
    }
  • geo_bounding_box实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    GET /hotel/_search
    {
    "query": {
    "geo_bounding_box": {
    "location": {
    "top_left": {
    "lat": 31.1,
    "lon": 121.5
    },
    "bottom_right": {
    "lat": 30.9,
    "lon": 121.7
    }
    }
    }
    }
    }

复合查询

function score:算分函数查询,可以控制文档相关性算分,控制文档排名

bool query:布尔查询,利用逻辑关系组合多个其它的查询,实现复杂搜索

function score

elasticsearch会根据词条和文档的相关度做打分,算法由两种:

  • TF-IDF算法

    TF-IDF算法

  • BM25算法,elasticsearch5.1版本后采用的算法

    BM25

  • function score实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    # 复合查询
    GET /hotel/_search
    {
    "query": {
    "function_score": {
    "query": { //原始查询条件
    "match": {
    "all": "外滩"
    }
    },
    "functions": [
    {
    "filter": { //过滤查到的信息
    "term": {
    "brand": "如家"
    }
    },
    "weight": 1
    }
    ]
    , "boost_mode": "sum" //sum模式为_score = 原始分数 + weight
    }
    }
    }

bool query

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

  • must:必须匹配每个子查询,类似“与”
  • should:选择性匹配子查询,类似“或”
  • must_not:必须不匹配,不参与算分,类似“非”
  • filter:必须匹配,不参与算分
  • bool query实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    # 名字中包含"如家", 价格400以上,范围10km之内
    GET /hotel/_search
    {
    "query": {
    "bool": {
    "must": [
    {
    "match": {
    "name": "如家"
    }
    }
    ],
    "must_not": [
    {
    "range": {
    "price": {
    "gt": 400
    }
    }
    }
    ],
    "filter": [
    {
    "geo_distance": {
    "distance": "10km",
    "location": {
    "lat": 31.21,
    "lon": 121.5
    }
    }
    }
    ]
    }
    }
    }

搜索结果处理

排序

sort

  • 实例1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 按分数降序,价格升序排序
    GET /hotel/_search
    {
    "query": {
    "match_all": {}
    }
    , "sort": [
    {
    "score": "desc"
    },
    {
    "price": "asc"
    }
    ]
    }
  • 实例2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # 按距离升序排序,单位"km"
    GET /hotel/_search
    {
    "query": {
    "match_all": {}
    }
    , "sort": [
    {
    "_geo_distance": {
    "location": {
    "lat": 31,
    "lon": 121
    },
    "order": "asc",
    "unit": "km"
    }
    }
    ]
    }

分页

from、size

深度分页问题:

elasticsearch内部分页时,要查990 ~ 1000的这10条,必须先查询 0~1000条,然后截取其中的990 ~ 1000的这10条。当es集群时,若要查前1000的文档,就得先查询所有节点的前1000,再将这些文档重新排名后选出前1000,对于节点有很大压力。

因此elasticsearch会禁止from+ size 超过10000的请求。

  • 实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 从0号开始查询20条数据
    GET /hotel/_search
    {
    "query": {
    "match_all": {}
    },
    "from": 0, # 起始值,默认0
    "size": 20, # 希望获取的文档总数
    "sort": [
    {
    "price": "asc"
    }
    ]

    }

高亮

highlight

  • 实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    GET /hotel/_search
    {
    "query": {
    "match": {
    "all": "如家"
    }
    },
    "highlight": {
    "fields": {
    "name": {
    "require_field_match": "false"
    }
    }
    }
    }
  • 注意:

    • 高亮是对关键字高亮,因此搜索条件必须带有关键字,而不能是范围这样的查询。
    • 默认情况下,高亮的字段,必须与搜索指定的字段一致,否则无法高亮
    • 如果要对非搜索字段高亮,则需要添加一个属性:required_field_match=false

RestAPI查询

不一样的地方就QueryBuilders的查询条件

查询所有

  • matchAllQuery实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 查询所有
*/
@Test
public void testMatchAll() throws IOException {
//创建request请求对象
SearchRequest request = new SearchRequest("hotel");
//组织DSL参数
request.source().query(QueryBuilders.matchAllQuery());
//发送查询请求,获取结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);

//解析结果
handResponse(response);
}

/**
* 解析结果
* @param response
*/
private void handResponse(SearchResponse response) {
//解析结果
SearchHits searchHits = response.getHits();
long value = searchHits.getTotalHits().value; //获取结果总数
SearchHit[] hits = searchHits.getHits(); //获取结果集
System.out.println("结果数: " + value);
for (SearchHit hit : hits) {
String json = hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
System.out.println(hotelDoc);
}
}

查询所有结果

解析响应结果,就是逐层解析JSON字符串,流程如下:

  • searchHits:通过response.getHits()获取,就是JSON中的最外层的hits,代表命中的结果
    • SearchHits#getTotalHits().value:获取总条数信息
    • SearchHits#getHits():获取SearchHit数组,也就是文档数组
      • SearchHit#getSourceAsString():获取文档结果中的_source,也就是原始的json文档数据

全文检索查询

  • matchQuery实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     @Test
    public void testMatch() throws IOException {
    //创建request请求对象
    SearchRequest request = new SearchRequest("hotel");
    //组织DSL参数
    request.source().query(QueryBuilders.matchQuery("all", "如家"));
    //发送查询请求,获取结果
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    //解析结果
    handResponse(response);
    }
  • multiMatchQuery实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Test
    public void testMultiMatch() throws IOException {
    //创建request请求对象
    SearchRequest request = new SearchRequest("hotel");
    //组织DSL参数
    request.source().query(QueryBuilders.multiMatchQuery("如家",
    "name", "business"));
    //发送查询请求,获取结果
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    //解析结果
    handResponse(response);
    }

精准查询

  • termQuery实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Test
    public void testTerm() throws IOException {
    //创建request请求对象
    SearchRequest request = new SearchRequest("hotel");
    //组织DSL参数
    request.source().query(QueryBuilders.termQuery("city", "上海"));
    //发送查询请求,获取结果
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    //解析结果
    handResponse(response);
    }
  • rangeQuery实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Test
    public void testTerm() throws IOException {
    //创建request请求对象
    SearchRequest request = new SearchRequest("hotel");
    //组织DSL参数
    request.source().query(QueryBuilders.rangeQuery("price")
    .gte(100)
    .lte(150));
    //发送查询请求,获取结果
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    //解析结果
    handResponse(response);
    }

复合查询

  • boolQuery实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Test
    public void testBool() throws IOException {
    //创建request请求对象
    SearchRequest request = new SearchRequest("hotel");
    //组织DSL参数
    request.source().query(QueryBuilders.boolQuery()
    .must(QueryBuilders.termQuery("city", "上海"))
    .filter(QueryBuilders.rangeQuery("price").lte(200)));
    //发送查询请求,获取结果
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    //解析结果
    handResponse(response);
    }

排序

  • sort实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Test
    public void testSort() throws IOException {
    //创建request请求对象
    SearchRequest request = new SearchRequest("hotel");
    //组织DSL参数
    request.source().sort("price", SortOrder.DESC);
    //发送查询请求,获取结果
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    //解析结果
    handResponse(response);
    }

分页

  • from、size实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Test
    public void testPage() throws IOException {
    int page = 1, size = 4;

    //创建request请求对象
    SearchRequest request = new SearchRequest("hotel");
    //组织DSL参数
    request.source().from((page - 1) * size).size(size);
    //发送查询请求,获取结果
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    //解析结果
    handResponse(response);
    }

高亮

  • highlighter实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Test
    public void testHighlight() throws IOException {
    //创建request请求对象
    SearchRequest request = new SearchRequest("hotel");
    //组织DSL参数
    request.source().query(QueryBuilders.matchQuery("all", "如家"))
    .highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
    //发送查询请求,获取结果
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    //解析结果
    handleResponse(response);
    }
  • 结果处理时得获取Highlight里的值


分布式搜索(1)
http://xwww12.github.io/2022/09/13/微服务/分布式搜索/
作者
xw
发布于
2022年9月13日
许可协议