《ElasticSearch6.x实战教程》之简单搜索、Java客户端(上)

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 《ElasticSearch6.x实战教程》之简单搜索、Java客户端(上)第五章-简单搜索众里寻他千百度搜索是ES的核心,本节讲解一些基本的简单的搜索。掌握ES搜索查询的RESTful的API犹如掌握关系型数据库的SQL语句,尽管Java客户端API为我们不需要我们去实际编写RESTful的API,但在生产环境中,免不了在线上执行查询语句做数据统计供产品经理等使用。

《ElasticSearch6.x实战教程》之简单搜索、Java客户端(上)
第五章-简单搜索
众里寻他千百度

搜索是ES的核心,本节讲解一些基本的简单的搜索。

掌握ES搜索查询的RESTful的API犹如掌握关系型数据库的SQL语句,尽管Java客户端API为我们不需要我们去实际编写RESTful的API,但在生产环境中,免不了在线上执行查询语句做数据统计供产品经理等使用。

数据准备
首先创建一个名为user的Index,并创建一个student的Type,Mapping映射一共有如下几个字段:

创建名为user的Index PUT http://localhost:9200/user

创建名为student的Type,且指定字段name和address的分词器为ik_smart。

POST http://localhost:9200/user/student/_mapping
{
"properties":{

 "name":{
     "type":"text",
     "analyzer":"ik_smart"
 },
 "age":{
     "type":"short"
 }

}
}
经过上一章分词的学习我们把text类型都指定为ik_smart分词器。

插入以下数据。

POST localhost:9200/user/student
{

"name":"kevin",
"age":25

}
POST localhost:9200/user/student
{

"name":"kangkang",
"age":26

}
POST localhost:9200/user/student
{

"name":"mike",
"age":22

}
POST localhost:9200/user/student
{

"name":"kevin2",
"age":25

}
POST localhost:9200/user/student
{

"name":"kevin yu",
"age":21

}
按查询条件数量维度
无条件搜索
GET http://localhost:9200/user/student/_search?pretty

查看索引user的student类型数据,得到刚刚插入的数据返回:

单条件搜索
ES查询主要分为term精确搜索、match模糊搜索。

term精确搜索
我们用term搜索name为“kevin”的数据。

POST http://localhost:9200/user/student/_search?pretty
{

"query":{
    "term":{
        "name":"kevin"
    }
}

}
既然term是精确搜索,按照非关系型数据库的理解来讲就等同于=,那么搜索结果也应该只包含1条数据。然而出乎意料的是,搜索结果出现了两条数据:name="kevin"和name="keivin yu",这看起来似乎是进行的模糊搜索,但又没有搜索出name="kevin2"的数据。我们先继续观察match的搜索结果。

match模糊搜索
同样,搜索name为“kevin”的数据。

POST http://localhost:9200/user/student/_search?pretty
{

"query":{
    "match":{
        "name":"kevin"
    }
}

}
match的搜索结果竟然仍然是两条数据:name="kevin"和name="keivin yu"。同样,name="kevin2"也没有出现在搜索结果中。

原因在于term和match的精确和模糊针对的是搜索词而言,term搜索不会将搜索词进行分词后再搜索,而match则会将搜索词进行分词后再搜索。例如,我们对name="kevin yu"进行搜索,由于term搜索不会对搜索词进行搜索,所以它进行检索的是"kevin yu"这个整体,而match搜索则会对搜索词进行分词搜索,所以它进行检索的是包含"kevin"和"yu"的数据。而name字段是text类型,且它是按照ik_smart进行分词,就算是"kevin yu"这条数据由于被分词后变成了"kevin"和"yu",所以term搜索不到任何结果。

如果一定要用term搜索name="kevin yu",结果出现"kevin yu",办法就是在定义映射Mapping时就为该字段设置一个keyword类型。

为了下文的顺利进行,删除DELETE http:localhost:9200/user/student重新按照开头创建索引以及插入数据吧。唯一需要修改的是在定义映射Mapping时,name字段修改为如下所示:

{

"properties":{
      "name":{
          "type":"text",
          "analyzer":"ik_smart",
          "fields":{
              "keyword":{
                  "type":"keyword",
        "ignore_abore":256
              }
          }
      },
"age":{
    "type":integer
}
}

}
待我们重新创建好索引并插入数据后,此时再按照term搜索name="kevin yu"。

POST http://localhost:9200/user/student/_search
{

"query":{
    "term":{
        "name.keyword":"kevin yu"
    }
}

}
返回一条name="kevin yu"的数据。按照match搜索同样出现name="kevin yu",因为name.keyword无论如何都不会再分词。

在已经建立索引且定义好映射Mapping的情况下,如果直接修改name字段,此时能修改成功,但是却无法进行查询,这与ES底层实现有关,如果一定要修改要么是新增字段,要么是重建索引。

所以,与其说match是模糊搜索,倒不如说它是分词搜索,因为它会将搜索关键字分词;与其将term称之为模糊搜索,倒不如称之为不分词搜索,因为它不会将搜索关键字分词。

match查询还有很多更为高级的查询方式:match_phrase短语查询,match_phrase_prefix短语匹配查询,multi_match多字段查询等。将在复杂搜索一章中详细介绍。

类似like的模糊搜索
wildcard通配符查询。

POST http://localhost:9200/user/student/_search?pretty
{
"query": {

"wildcard": {
  "name": "*kevin*"
}

}
}
ES返回结果包括name="kevin",name="kevin2",name="kevin yu"。

fuzzy更智能的模糊搜索
fuzzy也是一个模糊查询,它看起来更加”智能“。它类似于搜狗输入法中允许语法错误,但仍能搜出你想要的结果。例如,我们查询name等于”kevin“的文档时,不小心输成了”kevon“,它仍然能查询出结构。

POST http://localhost:9200/user/student/_search?pretty
{
"query": {

"fuzzy": {
  "name": "kevin"
}

}
}
ES返回结果包括name="kevin",name="kevin yu"。

多条件搜索
上文介绍了单个条件下的简单搜索,并且介绍了相关的精确和模糊搜索(分词与不分词)。这部分将介绍多个条件下的简单搜索。

当搜索需要多个条件时,条件与条件之间的关系有”与“,”或“,“非”,正如非关系型数据库中的”and“,”or“,“not”。

在ES中表示”与“关系的是关键字must,表示”或“关系的是关键字should,还有表示表示”非“的关键字must_not。

must、should、must_not在ES中称为bool查询。当有多个查询条件进行组合查询时,此时需要上述关键字配合上文提到的term,match等。

精确查询(term,搜索关键字不分词)name="kevin"且age="25"的学生。
POST http://localhost:9200/user/student/_search?pretty
{

"query":{
    "bool":{
        "must":[{
            "term":{
                "name.keyword":"kevin"
            }
        },{
            "term":{
                "age":25
            }
        }]
    }
}

}
返回name="kevin"且age="25"的数据。

精确查询(term,搜索关键字不分词)name="kevin"或age="21"的学生。
POST http://localhost:9200/user/student/_search?pretty
{

"query":{
    "bool":{
        "should":[{
            "term":{
                "name.keyword":"kevin"
            }
        },{
            "term":{
                "age":21
            }
        }]
    }
}

}
返回name="kevin",age=25和name="kevin yu",age=21的数据

精确查询(term,搜索关键字不分词)name!="kevin"且age="25"的学生。
POST http://localhost:9200/user/student/_search?pretty
{

"query":{
    "bool":{
        "must":[{
            "term":{
                "age":25
            }
        }],
        "must_not":[{
            "term":{
                "name.keyword":"kevin"
            }
        }]
    }
}

}
返回name="kevin2"的数据。

如果查询条件中同时包含must、should、must_not,那么它们三者是"且"的关系

多条件查询中查询逻辑(must、should、must_not)与查询精度(term、match)配合能组合成非常丰富的查询条件。

按等值、范围查询维度
上文中讲到了精确查询、模糊查询,已经"且","或","非"的查询。基本上都是在做等值查询,实际查询中还包括,范围(大于小于)查询(range)、存在查询(exists)、~不存在查询(missing)。

范围查询
范围查询关键字range,它包括大于gt、大于等于gte、小于lt、小于等于lte。

查询age>25的学生。
POST http://localhost:9200/user/student/_search?pretty
{

"query":{
    "range":{
        "age":{
            "gt":25
        }
    }
}

}
返回name="kangkang"的数据。

查询age >= 21且age < 26的学生。
POST http://localhost:9200/user/search/_search?pretty
{

"query":{
    "range":{
        "age":{
            "gte":21,
            "lt":25
        }
    }
}

}
查询age >= 21 且 age < 26且name="kevin"的学生

POST http://localhost:9200/user/search/_search?pretty
{

"query":{
    "bool":{
        "must":[{
            "term":{
                "name":"kevin"
            }
        },{
            "range":{
                "age":{
                    "gte":21,
                    "lt":25
                }
            }
        }]
    }
}

}
存在查询
存在查询意为查询是否存在某个字段。

查询存在name字段的数据。

POST http://localhost:9200/user/student/_search?pretty
{

"query":{
    "exists":{
        "field":"name"
    }   
}

}
不存在查询
不存在查询顾名思义查询不存在某个字段的数据。在以前ES有missing表示查询不存在的字段,后来的版本中由于must not和exists可以组合成missing,故去掉了missing。

查询不存在name字段的数据。

POST http://localhost:9200/user/student/_search?pretty
{

"query":{
    "bool":{
        "must_not":{
            "exists":{
                "field":"name"
            }
        }
    }   
}

}
分页搜索
谈到ES的分页永远都绕不开深分页的问题。但在本章中暂时避开这个问题,只说明在ES中如何进行分页查询。

ES分页查询包含from和size关键字,from表示起始值,size表示一次查询的数量。

查询数据的总数
POST http://localhost:9200/user/student/_search?pretty
返回文档总数。

分页(一页包含1条数据)模糊查询(match,搜索关键字不分词)name="kevin"
POST http://localhost:9200/user/student/_search?pretty
{

"query":{
    "match":{
        "name":"kevin"
    }
},
"from":0,
"size":1

}
结合文档总数即可返回简单的分页查询。

分页查询中往往我们也需要对数据进行排序返回,MySQL中使用order by关键字,ES中使用sort关键字指定排序字段以及降序升序。

分页(一页包含1条数据)查询age >= 21且age <=26的学生,按年龄降序排列。
POST http://localhost:9200/user/student/_search?pretty
{

"query":{
    "range":{
        "age":{
            "gte":21,
            "lte":26
        }
    }
},
"from":0,
"size":1,
"sort":{
    "age":{
        "order":"desc"
    }
}

}
ES默认升序排列,如果不指定排序字段的排序),则sort字段可直接写为"sort":"age"。

第六章-Java客户端(上)
ES提供了多种方式使用Java客户端:

TransportClient,通过Socket方式连接ES集群,传输会对Java进行序列化
RestClient,通过HTTP方式请求ES集群
目前常用的是TransportClient方式连接ES服务。但ES官方表示,在未来TransportClient会被永久移除,只保留RestClient方式。

同样,Spring Boot官方也提供了操作ES的方式Spring Data ElasticSearch。本章节将首先介绍基于Spring Boot所构建的工程通过Spring Data ElasticSearch操作ES,再介绍同样是基于Spring Boot所构建的工程,但使用ES提供的TransportClient操作ES。

Spring Data ElasticSearch
本节完整代码(配合源码使用更香):https://github.com/yu-linfeng/elasticsearch6.x_tutorial/tree/master/code/spring-data-elasticsearch

使用Spring Data ElasticSearch后,你会发现一切变得如此简单。就连连接ES服务的类都不需要写,只需要配置一条ES服务在哪儿的信息就能开箱即用。

作为简单的API和简单搜索两章节的启下部分,本节示例仍然是基于上一章节的示例。

通过IDEA创建Spring Boot工程,并且在创建过程中选择Spring Data ElasticSearch,主要步骤如下图所示:

第一步,创建工程,选择Spring Initializr。

第二步,选择SpringBoot的依赖NoSQL -> Spring Data ElasticSearch。

idea-springboot-es

创建好Spring Data ElasticSearch的Spring Boot工程后,按照ES惯例是定义Index以及Type和Mapping。在Spring Data ElasticSearch中定义Index、Type以及Mapping非常简单。ES文档数据实质上对应的是一个数据结构,也就是在Spring Data ElasticSearch要我们把ES中的文档数据模型与Java对象映射关联。

定义StudentPO对象,对象中定义Index以及Type,Mapping映射我们引入外部json文件(json格式的Mapping就是在简单搜索一章中定义的Mapping数据)。

package com.coderbuff.es.easy.domain;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Mapping;

import java.io.Serializable;

/**

  • ES mapping映射对应的PO
  • Created by OKevin on 2019-06-26 22:52
    */

@Getter
@Setter
@ToString
@Document(indexName = "user", type = "student")
@Mapping(mappingPath = "student_mapping.json")
public class StudentPO implements Serializable {

private String id;

/**
 * 姓名
 */
private String name;

/**
 * 年龄
 */
private Integer age;

}
Spring Data ElasticSearch为我们屏蔽了操作ES太多的细节,以至于真的就是开箱即用,它操作ES主要是通过ElasticsearchRepository接口,我们在定义自己具体业务时,只需要继承它,扩展自己的方法。

package com.coderbuff.es.easy.dao;

import com.coderbuff.es.easy.domain.StudentPO;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

/**

  • Created by OKevin on 2019-06-26 23:45
    */

@Repository
public interface StudentRepository extends ElasticsearchRepository {
}
ElasticsearchTemplate可以说是Spring Data ElasticSearch最为重要的一个类,它对ES的Java API进行了封装,创建索引等都离不开它。在Spring中要使用它,必然是要先注入,也就是实例化一个bean。而Spring Data ElasticSearch早为我们做好了一切,只需要在application.properties中定义spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300,就可大功告成(网上有人的教程还在使用applicationContext.xml定义一个bean,事实证明,受到了Spring多年的“毒害”,Spring Boot远比我们想象的智能)。

单元测试创建Index、Type以及定义Mapping。

package com.coderbuff.es;

import com.coderbuff.es.easy.domain.StudentPO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.test.context.junit4.SpringRunner;

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

@Autowired
private ElasticsearchTemplate elasticsearchTemplate;

/**
 * 测试创建Index,type和Mapping定义
 */
@Test
public void createIndex() {
    elasticsearchTemplate.createIndex(StudentPO.class);
    elasticsearchTemplate.putMapping(StudentPO.class);
}

}
使用GET http://localhost:9200/user请求命令,可看到通过Spring Data ElasticSearch创建的索引。

索引创建完成后,接下来就是定义操作student文档数据的接口。在StudentService接口的实现中,通过组合StudentRepository类对ES进行操作。StudentRepository类继承了ElasticsearchRepository接口,这个接口的实现已经为我们提供了基本的数据操作,保存、修改、删除只是一句代码的事。就算查询、分页也为我们提供好了builder类。"最难"的实际上不是实现这些方法,而是如何构造查询参数SearchQuery。创建SearchQuery实例,有两种方式:

构建NativeSearchQueryBuilder类,通过链式调用构造查询参数。
构建NativeSearchQuery类,通过构造方法传入查询参数。
这里以"不分页range范围和term查询age>=21且age<26且name=kevin"为例。

SearchQuery searchQuery = new NativeSearchQueryBuilder()

            .withQuery(QueryBuilders.boolQuery()
                    .must(QueryBuilders.rangeQuery("age").gte(21).lt(26))
                    .must(QueryBuilders.termQuery("name", "kevin"))).build();

搜索条件的构造一定要对ES的查询结构有比较清晰的认识,如果是在了解了简单的API和简单搜索两章的前提下,学习如何构造多加练习一定能掌握。这里就不一一验证前面章节的示例,一定要配合代码使用练习(https://github.com/yu-linfeng/elasticsearch6.x_tutorial/tree/master/code/spring-data-elasticsearch)
原文地址https://www.cnblogs.com/yulinfeng/p/11219786.html

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
9天前
|
IDE Oracle Java
java基础教程(1)-Java概述和相关名词解释
【4月更文挑战第1天】Java是1995年Sun Microsystems发布的高级编程语言,以其跨平台特性著名。它介于编译型和解释型语言之间,通过JVM实现“一次编写,到处运行”。Java有SE、EE和ME三个版本,分别针对标准、企业及嵌入式应用。JVM是Java虚拟机,确保代码在不同平台无需重编译。JRE是运行环境,而JDK包含开发工具。要安装Java开发环境,可从Oracle官网下载JDK,设置JAVA_HOME环境变量并添加到PATH。
|
30天前
|
Java
Java String split()方法详细教程
Java String split()方法详细教程
20 0
|
13天前
|
设计模式 安全 Java
Java并发编程实战:使用synchronized关键字实现线程安全
【4月更文挑战第6天】Java中的`synchronized`关键字用于处理多线程并发,确保共享资源的线程安全。它可以修饰方法或代码块,实现互斥访问。当用于方法时,锁定对象实例或类对象;用于代码块时,锁定指定对象。过度使用可能导致性能问题,应注意避免锁持有时间过长、死锁,并考虑使用`java.util.concurrent`包中的高级工具。正确理解和使用`synchronized`是编写线程安全程序的关键。
|
17天前
|
Web App开发 前端开发 Java
《手把手教你》系列技巧篇(九)-java+ selenium自动化测试-元素定位大法之By name(详细教程)
【4月更文挑战第1天】 这篇教程介绍了如何使用Selenium Webdriver通过name属性来定位网页元素,作为系列教程的一部分,之前讲解了id定位,后续还会有其他六种定位方法。文中以百度搜索为例,详细说明了定位搜索框(name=&quot;wd&quot;)并输入关键词“北京宏哥”的步骤,包括手动操作流程、编写自动化脚本以及代码实现。此外,还提供了查看和理解Selenium源码的方法,强调了`open implementation`选项用于查看方法的具体实现。整个过程旨在帮助读者学习Selenium的元素定位,并实践自动化测试。
37 0
|
29天前
|
Web App开发 存储 JavaScript
《手把手教你》系列技巧篇(八)-java+ selenium自动化测试-元素定位大法之By id(详细教程)
【2月更文挑战第17天】本文介绍了Web自动化测试的核心——元素定位。文章首先强调了定位元素的重要性,指出找不到元素则无法进行后续操作。Selenium提供八种定位方法,包括By id、name、class name等。其中,By id是最简单快捷的方式。文章还阐述了自动化测试的步骤:定位元素、操作元素、验证结果和记录测试结果。此外,讨论了如何选择定位方法,推荐优先使用简单稳定的方式,如id,其次考虑其他方法。最后,作者提供了Chrome浏览器的开发者工具作为定位元素的工具,并给出了通过id定位的代码示例。
51 0
|
12天前
|
前端开发 Java 测试技术
《手把手教你》系列技巧篇(十二)-java+ selenium自动化测试-元素定位大法之By link text(详细教程)
【4月更文挑战第4天】本文介绍了link text在自动化测试中的应用。Link text是指网页中链接的文字描述,点击可跳转至其他页面。文章列举了8种常用的定位方法,其中着重讲解了link text定位,并通过实例展示了如何使用Java代码实现点击百度首页的“奥运奖牌榜 最新排名”链接,进入相应页面。如果link text不准确,则无法定位到元素,这说明linkText是精准匹配,而非模糊匹配。文章还提到了partial link text作为link text的模糊匹配版本,将在后续内容中介绍。
35 4
|
11天前
|
XML 前端开发 Java
《手把手教你》系列技巧篇(十四)-java+ selenium自动化测试-元素定位大法之By xpath上卷(详细教程)
【4月更文挑战第6天】按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍定位倒数二个方法:By xpath。xpath 的定位方法, 非常强大。使用这种方法几乎可以定位到页面上的任意元素。xpath 是XML Path的简称, 由于HTML文档本身就是一个标准的XML页面,所以我们可以使用Xpath 的用法来定位页面元素。XPath 是XML 和Path的缩写,主要用于xml文档中选择文档中节点。基于XML树状文档结构,XPath语言可以用在整棵树中寻找指定的节点。
42 0
|
30天前
|
Web App开发 安全 Java
《手把手教你》系列技巧篇(七)-java+ selenium自动化测试-宏哥带你全方位吊打Chrome启动过程(详细教程)
【2月更文挑战第16天】本文介绍了如何通过查看源码理解Selenium启动Chrome浏览器的过程。首先,展示了启动Chrome的Java代码,包括设置系统属性、创建WebDriver实例、最大化窗口、设置隐性等待、打开网站、获取页面标题以及关闭浏览器。文章还讲解了包(package)、import导入、setProperty设置系统属性、WebDriver接口、driver实例、manage方法、get方法加载网页以及quit方法退出浏览器的基本概念和作用。适合没有Java基础的读者了解Selenium与Java的交互方式。
45 3
|
2天前
|
Java 测试技术 定位技术
《手把手教你》系列技巧篇(二十三)-java+ selenium自动化测试-webdriver处理浏览器多窗口切换下卷(详细教程)
【4月更文挑战第15天】本文介绍了如何使用Selenium进行浏览器窗口切换以操作不同页面元素。首先,获取浏览器窗口句柄有两种方法:获取所有窗口句柄的集合和获取当前窗口句柄。然后,通过`switchTo().window()`方法切换到目标窗口句柄。在项目实战部分,给出了一个示例,展示了在百度首页、新闻页面和地图页面之间切换并输入文字的操作。最后,文章还探讨了在某些情况下可能出现的问题,并提供了一个简单的本地HTML页面示例来演示窗口切换的正确操作。
22 0
|
3天前
|
存储 Java
Java基础教程(7)-Java中的面向对象和类
【4月更文挑战第7天】Java是面向对象编程(OOP)语言,强调将事务抽象成对象。面向对象与面向过程的区别在于,前者通过对象间的交互解决问题,后者按步骤顺序执行。类是对象的模板,对象是类的实例。创建类使用`class`关键字,对象通过`new`运算符动态分配内存。方法包括构造函数和一般方法,构造函数用于对象初始化,一般方法处理逻辑。方法可以有0个或多个参数,可变参数用`类型...`定义。`this`关键字用于访问当前对象的属性。