Java 操作ElasticSearch

  • Post author:
  • Post category:java


Java REST提供了两种风格的客户端连接工具,Java High Level REST Client、Java Low Level REST Client,这里我就不去细说Java Low Level REST Client了,因为这我确实没用到过,也不是很了解,我说一下Java High Level REST Client。

Java High Level REST Client ​​​​

首先如果你喜欢看官方文档的话,我把地址粘贴在这里,官网讲的也比较详细:

Java High Level REST Client | Java REST Client [7.4] | Elastic

第一步添加依赖:

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

<!– https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-high-level-client –>

<dependency>

<groupId>org.elasticsearch.client</groupId>

<artifactId>elasticsearch-rest-high-level-client</artifactId>

<version>7.4.0</version>

</dependency>

<!– https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-client –>

<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>

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>fastjson</artifactId>

<version>1.2.69</version>

</dependency>

第二步:

在配置文件添加对应的地址以及端口:

elasticsearch:

host: 127.0.0.1

port: 9200

第三步使用:

1.创建一个索引

	@Autowired
	private RestHighLevelClient client;  //注入client、后面的代码会省略
	
  @Test
	public void addIndexAndmapping() throws IOException {
		IndicesClient indices = client.indices();
		//创建索引
		CreateIndexRequest createIndexRequest = new CreateIndexRequest("test_index");
		//设置分片数量以及副本数量
		createIndexRequest.settings(Settings.builder()
				.put("index.number_of_shards", 3)
				.put("index.number_of_replicas", 2)
		);
		//添加映射,相当于给表创建字段
		String mapping  ="{properties={address={analyzer=ik_max_word, type=text}, name={type=keyword}, age={type=integer}}}";
		System.out.println(mapping);
		createIndexRequest.mapping(mapping, XContentType.JSON);
		CreateIndexResponse response = indices.create(createIndexRequest, RequestOptions.DEFAULT);//创建索引
		System.out.println(response.isAcknowledged());
	}

2.查询某个索引的信息

	@Test
	public void queryIndex() throws IOException {
		IndicesClient indices = client.indices();
		GetIndexRequest getIndexRequest = new GetIndexRequest("person");
		GetIndexResponse getIndexResponse = indices.get(getIndexRequest, RequestOptions.DEFAULT);
		Map<String, MappingMetaData> mappings = getIndexResponse.getMappings();
		mappings.forEach((k,v)->{
			System.out.println("key:"+k+",v:"+v.getSourceAsMap());
		});
	}

3.添加文档

	@Test
	public void addDoc() throws IOException {
		//数据对象
		Person p = new Person();
		p.setName("meiting");
		p.setAddress("sichuan");
		p.setAge(22);
		IndexRequest request = new IndexRequest("person").id("1").source(JSON.toJSONString(p),XContentType.JSON);
		IndexResponse index = client.index(request, RequestOptions.DEFAULT);
	}

4.修改文档

就跟添加文档一样,在es中如果指定文档的id存在就会就会更新这个文档。操作方式跟上面添加文档一样,不过需要指定文档的id。

5.查询操作


5.1根据id查询文档或者删除文档

 @Test
	public void findDocByID() throws IOException {
		GetRequest getIndexRequest = new GetRequest("person","1");
		GetResponse documentFields = client.get(getIndexRequest, RequestOptions.DEFAULT);
		System.out.println(documentFields.getSourceAsString());
	}
  //根据id删除文档
	@Test
	public void delDocByID() throws IOException {
		DeleteRequest deleteRequest = new DeleteRequest("person","1");
		DeleteResponse documentFields = client.delete(deleteRequest, RequestOptions.DEFAULT);
		System.out.println(documentFields.getId());
	}


5.2查询所有文档

	@Test
	public void findAllDoc() throws IOException {
		SearchRequest sr = new SearchRequest("person");

		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		sourceBuilder.from(0).size(20);//指定分页
		MatchAllQueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
		sourceBuilder.query(queryBuilder);
		sr.source(sourceBuilder);
		SearchResponse search = client.search(sr, RequestOptions.DEFAULT);
		System.out.println(search.getHits().getTotalHits()); //查询到的所有的文档数量
		SearchHits hits = search.getHits();
		hits.forEach(ele->{
			System.out.println(ele.getSourceAsString());
		});
	}


5.3term查询(词条查询) text支持分词,keyword不支持分词

    @Test
	public void termQuery() throws IOException {
		SearchRequest requst = new SearchRequest("person");
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

		QueryBuilder queryBuilder = QueryBuilders.termQuery("name","阿");
		sourceBuilder.query(queryBuilder);

		requst.source(sourceBuilder);
		SearchResponse search = client.search(requst, RequestOptions.DEFAULT);

		SearchHit[] hits = search.getHits().getHits();
		for (SearchHit hit : hits) {
			System.out.println(hit.getSourceAsString());
		}
	}
/** 类似于
get person/_search
{
  "query":{
    "term":{
      "name":{
        "value":"阿"
      }
    }
  }
}
*/


5.4match查询

	@Test
	public void matchQuery() throws IOException {
		SearchRequest requst = new SearchRequest("person");
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("name", "老板");
		queryBuilder.operator(Operator.AND);//求并集
		sourceBuilder.query(queryBuilder);
		requst.source(sourceBuilder);
		SearchResponse search = client.search(requst, RequestOptions.DEFAULT);
		SearchHit[] hits = search.getHits().getHits();
		for (SearchHit hit : hits) {
			System.out.println(hit.getSourceAsString());
		}
	}
/**类似于
get person/_search
{
  "query":{
    "match": {
      "name": {
        "query":"永恩",
        "operator": "and"
      }
    }
  }
}
*/


5.5模糊查询

wildcard查询:会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) *(0个或者多个字符)

regexp查询:正则查询

prefix查询:前缀查询

	@Test
	public void wildSearch() throws IOException {
		SearchRequest requst = new SearchRequest("person");
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

		QueryBuilder queryBuilder = QueryBuilders.wildcardQuery("name","菲*");
		//QueryBuilder queryBuilder = QueryBuilders.regexpQuery("name","菲*");  //正则查询
		//QueryBuilder queryBuilder = QueryBuilders.prefixQuery("name","菲"); //前缀查询
		sourceBuilder.query(queryBuilder);
		requst.source(sourceBuilder);
		SearchResponse search = client.search(requst, RequestOptions.DEFAULT);
		SearchHit[] hits = search.getHits().getHits();
		for (SearchHit hit : hits) {
			System.out.println(hit.getSourceAsString());
		}
	}


5.6范围查询

range范围查询:查找指定字段在指定范围内包含值。查询年龄20到26的人并排序

	@Test
	public void rangeQuery() throws IOException {
		SearchRequest requst = new SearchRequest("person");

		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		QueryBuilder queryBuilder = QueryBuilders.rangeQuery("age").gte(20).lte(26);
		sourceBuilder.query(queryBuilder);
		sourceBuilder.sort("age", SortOrder.DESC); //排序
		requst.source(sourceBuilder);
		SearchResponse search = client.search(requst, RequestOptions.DEFAULT);
		SearchHit[] hits = search.getHits().getHits();
		for (SearchHit hit : hits) {
			System.out.println(hit.getSourceAsString());
		}
	}
/**类似于
get person/_search
{
  "query":{
    "range": {
      "age": {
        "gte": 20,
        "lte": 24
      }
    }
  },
  "sort":[  //排序
    {
      "age":{
        "order":"desc"
      }
    }
    ]
}
*/


5.7querySt ring查询

queryString查询就是:对查询条件进行分词,然后将分词后的查询条件和词条进行等值匹配。然后取并集。

这种查询的特点就是可以指定多个字段查询。比如下面就是查名字和地址中包含“老”的数据。

	@Test
	public void queryStringQuery() throws IOException {
		SearchRequest requst = new SearchRequest("person");

		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

		QueryBuilder queryBuilder = QueryBuilders.queryStringQuery("老").field("name").field("address").defaultOperator(Operator.OR);
		sourceBuilder.query(queryBuilder);
		sourceBuilder.sort("age", SortOrder.DESC); //排序
		requst.source(sourceBuilder);
		SearchResponse search = client.search(requst, RequestOptions.DEFAULT);
		SearchHit[] hits = search.getHits().getHits();
		for (SearchHit hit : hits) {
			System.out.println(hit.getSourceAsString());
		}
	}
/**类似于
get person/_search
{
  "query":{
    "query_string": {
      "fields": ["name","address"],
      "query": "永恩 OR 瑟提"
    }
  }
}
*/

除了上面说的这些常用的查询操作外还包括许多其他的查询,比如说聚合查询、高亮查询等等。在官网都可以查到对应的例子。到时候可以去官网看对应的例子。

Spring Data 操作ES

Spring Data也提供了操作ES数据库的功能,因为我们现在的项目基本都是基于Spring的,所以集成这个功能之后很多项目在考虑选择的时候也会把这种方式考了进来,我之前参与的项目就是采用的这种方式来操作ES数据库的。

1.搭建环境

添加依赖:添加依赖的时候要注意版本的选择,要选择对应的版本,参考官网

Spring Data Elasticsearch – Reference Documentation

<?xml version=”1.0″ encoding=”UTF-8″?>

<project xmlns=”http://maven.apache.org/POM/4.0.0″ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd”>

<modelVersion>4.0.0</modelVersion>

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.7.11</version>

<relativePath/> <!– lookup parent from repository –>

</parent>

<groupId>com.example</groupId>

<artifactId>springDataEs</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>war</packaging>

<name>springDataEs</name>

<description>Demo project for Spring Boot</description>

<properties>

<java.version>1.8</java.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-elasticsearch</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<optional>true</optional>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

</dependencies>

</project>

然后第二步配置文件

spring.elasticsearch.rest.uris=127.0.0.1:9200

前面要做的步骤就这两步。

2.实际操作


2.1创建实体以及接口

@Document(indexName = "animal",createIndex = true,shards = 1,replicas = 1)
@Data
public class Animal {
    @Id
    private long id;

    @Field(type= FieldType.Text)
    private String name;

    @Field(type=FieldType.Text)
    private String address;

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

这上面需要注意的就是几个注解:

  • @Document 注解作用在类上,说明这是一个文档对象。

indexName:索引名称

createIndex:是否需要创建索引,若为true就会自动创建索引

shards、replicas:分片数量和副本数量

  • @Field(type= FieldType.Text)

    在es对应的这个字段的数据类型,这里FieldType.Tex在es创建后这个字段就对应为text类型的。


2.2创建存储库接口

创建了这个接口并继承

ElasticsearchRepository

之后,就可以使用了。

public interface AnimalRepostory extends ElasticsearchRepository<Animal,Long> {
}

继承关系:

然后就可以基本的使用了,步骤非常简单。。。


2.3新增文档

我新写了一个接口,去尝试添加一个文档。

    @PostMapping("addDoc")
    public void addDoc(){
        Animal animal = new Animal();
        animal.setId(1);
        animal.setName("tew");
        animal.setAddress("111");
        animal.setAge(12);
        animalRepostory.save(animal);
    }

效果

可以看到,我们添加文档成功了,这个索引也自动给我们创建了。

Spring Data ElasticSearch对ES的操作进行了很完美的包装,他是基于Java High Level REST Client写的,使用起来非常方便。这里我就不对其他的方法进行讲解了,它的crud自己去搭建个环境跑起来,只要环境搭好之后后面就很方便了。对于简单的查询我们可以直接创建对应的接口方法就可以了。下面给出官网给出的例子:

interface PersonRepository extends Repository<Person, Long> {

  List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

  // Enables the distinct flag for the query
  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  // Enabling ignoring case for an individual property
  List<Person> findByLastnameIgnoreCase(String lastname);
  // Enabling ignoring case for all suitable properties
  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  // Enabling static ORDER BY for a query
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}

分页查询和排序,只需要在定义接口方法的时候给出Pageable以及Sort规则就可以了。

例子:

	@PostMapping("findPage")
    public void findPage(){
        Pageable page =  Pageable.ofSize(3).withPage(1);
        Iterable<Animal> all = animalRepostory.findAll(page);
        for(Iterator<Animal> iterator = all.iterator(); iterator.hasNext();System.out.println(iterator.next())){
        }
    }

    @PostMapping("findPageSort")
    public void findPageSort(){
        Sort sort = Sort.by("age").descending();
        Iterable<Animal> all = animalRepostory.findAll(sort);
        for(Iterator<Animal> iterator = all.iterator(); iterator.hasNext();System.out.println(iterator.next())){
        }
    }

自定义DSL查询:

这里用一个模糊查询举例:

在对应的接口创建一个接口,并在@Query注解里面写上对应的查询语句

public interface AnimalRepostory extends ElasticsearchRepository<Animal,Long> {
    @Query("{\n" +
            "    \"wildcard\": {\n" +
            "      \"name\": {\n" +
            "        \"value\": \"t*\"\n" +
            "      }\n" +
            "    }\n" +
            "  }")
    List<Animal> findTest();
}

效果:

    @PostMapping("findQuery")
    public void findQuery(){
        List<Animal> test = animalRepostory.findTest();
        test.forEach(ele->{
            System.out.println(ele);
        });
    }

Spring Data还提供了很多其他的查询功能,如果有需要用到的我建议可以去看看官网。



版权声明:本文为xhl1123456789原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。