由ES提供丰富且灵活的查询语言叫做DSL查询(Query DSL)。将查询语句通过http request body发送到ES,主要包含如下参数:

  • query符合Query DSL语法的查询语句
  • from、size
  • timeout
  • sort
  • 。。。

例如:

  1. GET woniu48/_search
  2. {
  3. "query": {
  4. "term": {
  5. "name": "lisi"
  6. }
  7. }
  8. }

基于JSON定义的查询语言,主要包含如下两种类型

  1. 字段类查询
    如term、match、range等,只针对某一个字段进行查询
  2. 复合查询
    如bool查询等,包含一个或多个字段类查询或则复合查询语句

    7.1 字段类查询

    字段类查询主要包括以下两类:

  3. 全文匹配

    • 针对text类型的字段进行全文检索,会对查询语句先进行分词处理,如match、match_phrase等query类型
  4. 单词匹配
    • 不会对查询语句做分词处理,直接匹配字段的倒排索引,如term、terms、range等query类型

全文匹配(Match Query)

  1. GET woniu48/_search
  2. {
  3. "query": {
  4. "match": {
  5. "username": "alfred way"
  6. }
  7. }
  8. }

Match Query流程
DSL查询 - 图1
通过operator参数可以控制单词间的匹配关系,可选项为or或则and

GET woniu48/_search
{
  "query": {
    "match": {
      "desc": {
        "query": "非常漂亮",
        "operator": "and"
      }
    }
  }
}

上面查询如果分词器分词为“非常”、“漂亮”两个词,查询匹配结果就必须同时包含这两个词的文档
单词匹配(Term Query)

GET woniu48/_search
{
  "query": {
    "term": {
      "name": "lisi"
    }
  }
}

一次传入多个单词进行查询

GET woniu48/_search
{
  "query": {
    "terms": {
      "name": [
        "lisi",
        "wangmazi"
      ]
    }
  }
}

Range Query(范围查询)

GET woniu48/_search
{
  "query": {
    "range": {
      "age": {
        "gte": 20,
        "lte": 23
      }
    }
  }
}

7.2 复合查询

复合查询是指包含字段类查询或复合查询的类型,主要包括以下几类:

  1. constant_score query
  2. bool query *
  3. dis_max query
  4. function_score query
  5. boosting query

Bool Query
布尔查询由一个或多个布尔子句组成,主要包含如下4个:

Name Description
filter 只过滤符合条件的文档,不计算相关性得分
must 文档必须复合must中的所有条件,会影响相关性得分
must_not 文档必须不符合must_not中的所有条件
should 文档可以符合should中的条件,会影响相关性得分
  1. FilterFilter查询只过滤符合条件的文档,不会进行相关性算分

    • ES针对Filter会有智能缓存,因此其执行效率很高
    • 做简单匹配查询且不考虑算分时,推荐使用filter替代query等
      GET woniu48/_search
      {
      "query": {
      "bool": {
       "filter": [
         {
           "term": {
             "name": "admin"
           }
         },
         {
           "range": {
             "age": {
               "gte": 25
             }
           }
         }
       ]
      }
      }
      }
      
  2. must

    GET woniu48/_search
    {
    "query": {
     "bool": {
       "must": [
         {
           "match": {
             "desc": "漂亮的人"
           }
         },
         {
           "range": {
             "age": {
               "gte": 22
             }
           }
         }
       ]
     }
    }
    }
    
  3. must_not
    查询描述里面经过分词有“漂亮”一词以及不带“非常”一词的结果

    GET woniu48/_search
    {
    "query": {
     "bool": {
       "must": [
         {
           "match": {
             "desc": "漂亮的人"
           }
         }
       ],
       "must_not": [
         {
           "match": {
             "desc": "非常好"
           }
         }
       ]
     }
    }
    }
    
  4. should

    • 只包含should时,文档必须至少满足一个条件(minimun_should_match可以控制满足条件的个数或则百分比)
      例如:下面至少满足一个条件

      GET woniu48/_search
      {
      "query": {
      "bool": {
       "should": [
         {
           "match": {
             "desc": "漂亮的人"
           }
         },
         {
           "range": {
             "age": {
               "lte": 20
             }
           }
         }
       ]
      }
      }
      }
      
    • 例如:下面需要满足两个条件

      GET woniu48/_search
      {
      "query": {
      "bool": {
       "should": [
         {
           "match": {
             "desc": "漂亮的人"
           }
         },
         {
           "range": {
             "age": {
               "lte": 20
             }
           }
         }
       ],
       "minimum_should_match": 2
      }
      }
      }
      
    • 同时包含should和must时,文档不必满足should中的条件,但是如果满足条件,会增加相关性得分
      该查询只会以must匹配来搜索

      GET woniu48/_search
      {
      "query": {
      "bool": {
       "should": [
         {
           "range": {
             "age": {
               "lte": 19
             }
           }
         }
       ],
       "must": [
         {
           "match": {
             "desc": "漂亮的人"
           }
         }
       ]
      }
      }
      }
      
  5. 综合实例
    查询年龄大于20并且包含“漂亮的人”描述或则年龄小于18的学员,分页显示第2页,每页显示2条,且按照年龄由低到高排序

    GET woniu48/_search
    {
    "query": {
     "bool": {
       "should": [
         {
           "bool": {
             "must": [
               {
                 "match": {
                   "desc": "漂亮的人"
                 }
               },
               {
                 "range": {
                   "age": {
                     "gte": 20
                   }
                 }
               }
             ]
           }
         },
         {
           "range": {
             "age": {
               "lte": 18
             }
           }
         }
       ]
     }
    },
    "from": 2,
    "size": 2,
    "sort": [
     {
       "age": {
         "order": "desc"
       }
     }
    ]
    }
    

    8 SpringBoot集成

    可以通过Spring Data Elasticsearch来进行操作,boot对其进行了集成:

  6. 导包

    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    
  7. 配置连接

    spring:
    elasticsearch:
     rest:
       uris:
       - http://localhost:9200
    
  8. 注入模板对象

    @Autowired
    ElasticsearchOperations operations;
    

    8.1 对象映射

    ES的文档对应于Java的对象,可以通过Spring-Data-Es提供的注解来进行关联:

    @Data
    @Document(indexName = "woniu48")
    public class Student {
     @Id
     @Field(type = FieldType.Integer,name = "id")
     private Integer id;
     @Field(type = FieldType.Keyword,name="name")
     private String name;
     @Field(type = FieldType.Integer,name="age")
     private Integer age;
     @Field(type = FieldType.Text,analyzer = "ik_max_word",name = "desc")
     private String desc;
    }
    
  • @Document: 在类级别应用,以指示该类是映射到ES的哪一个Index。最重要的属性是:
    • indexName: 用于存储此实体的索引的名称
  • @Id: 用来标识ID
  • @Field: 在字段级别应用并定义字段的属性:
    • name: 字段名称
    • type: 字段类型
    • analyzer: 分词器

      8.2 操作

      保存(新增与更新)对象:
      Student stu = new Student();
      stu.setId(3333);
      stu.setName("张三丰");
      stu.setDesc("此人涨得不好看");
      stu.setAge(100);
      operations.save(stu);
      
      删除对象:
      //第一个参数是id
      operations.delete("3333", Student.class);
      
      查询对象:
      查询有三种主要方式:
  1. 通过CriteriaQuery条件来查询,可以简单组合条件,不能分词,适用于非常简单的查询

    //查询年龄在20-25之间或则名称是"admin"的学员
    Criteria c = new Criteria("age").between(20, 25)
     .or("name").is("admin");            
    CriteriaQuery cq = new CriteriaQuery(c);
    SearchHits<Student> shs = operations.search(cq, Student.class);
    System.out.println(shs.getTotalHits());
    shs.getSearchHits().forEach(student -> {
     System.out.println(student.getContent().getName()+":"+student.getContent().getAge());
    });
    
  2. 通过StringQuery来查询,直接在java这边构建查询json字符串,不推荐使用

  3. 通过NativeSearchQuery来构建复杂查询,包括bool查询等等,推荐使用

    /*查询匹配非常漂亮的人或则年龄在90以上的*/
    //构建NativeSearchQuery建造器
    NativeSearchQueryBuilder nsqb = new NativeSearchQueryBuilder();
    //构造Bool查询条件        
    BoolQueryBuilder bqb = new BoolQueryBuilder();
    //通过该Bool查询器构建should查询器
    List<QueryBuilder> should = bqb.should();
    //向should里面添加两个查询条件
    should.add(new MatchQueryBuilder("desc", "非常漂亮的人"));
    should.add(new RangeQueryBuilder("age").gt(90));
    //注册bool查询条件
    nsqb.withQuery(bqb);
    //注册分页(第一页,显示3条)
    nsqb.withPageable(PageRequest.of(0, 3));
    //注册排序(age降序)
    nsqb.withSort(SortBuilders.fieldSort("age").order(SortOrder.DESC));
    //构建NativeSearchQuery
    NativeSearchQuery nsq = nsqb.build();
    //执行查询方法        
    SearchHits<Student> shs = operations.search(nsq, Student.class);
    System.out.println(shs.getTotalHits());
    shs.getSearchHits().forEach(x -> {
     Student stu = x.getContent();
     System.out.println(stu.getName()+":"+stu.getDesc()+":"+stu.getAge());
    });
    
  4. 查询结果高亮显示

    //创建高亮显示器
    HighlightBuilder hb = new HighlightBuilder();
    //设置高亮字段
    hb.field("desc");
    //设置为false,匹配字段都会高亮显示
    hb.requireFieldMatch(false);
    //设置如何高亮显示
    hb.preTags("<span style=\"color:red\">");
    hb.postTags("</span>");
    //设置高亮显示范围以字符为单位
    hb.fragmentSize(800);
    //设置高亮显示的开始位置
    hb.numOfFragments(0);
    NativeSearchQueryBuilder nsqb = new NativeSearchQueryBuilder();
    //注册高亮显示器
    nsqb.withHighlightBuilder(hb);
    nsqb.withQuery(new MatchQueryBuilder("desc", "非常漂亮"));
    NativeSearchQuery nsq = nsqb.build();
    SearchHits<Student> shs = operations.search(nsq, Student.class);
    shs.getSearchHits().forEach(x -> {
     Student stu = x.getContent();
     String desc = stu.getDesc();
     //获取高亮显示字段
     List<String> hs = x.getHighlightField("desc"); 
     if(hs != null && hs.size() > 0) desc = hs.get(0);
     System.out.println(stu.getName()+":"+desc);
    });