Spring Data ElasticSearch

  • 1. Spring Data简介
  • 1.2 Spring Data ElasticSearch
  • 2. 简单查询案例
  • 3. 聚合查询


1. Spring Data简介

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持map-reduce框架和云计算数据服务。 Spring Data可以极大的简化JPA的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了CRUD外,还包括如分页、排序等一
些常用的功能。

Spring Data的官网:

http://projects.spring.io/spring-data/

Spring Data常用的功能模块如下:

spring和es版本对比 spring data es_spring和es版本对比


spring和es版本对比 spring data es_java_02

1.2 Spring Data ElasticSearch

Spring Data ElasticSearch 基于 spring data API 简化 elasticSearch操作,将原始操作elasticSearch的客户端API 进行封装 。Spring Data为Elasticsearch项目提供集成搜索引擎。Spring Data ElasticsearchPOJO的关键功能区域为中心的模型与Elastichsearch交互文档和轻松地编写一个存储库数据访问层。

官方网站:

http://projects.spring.io/spring-data-elasticsearch/

2. 简单查询案例

1)导入Spring Data ElasticSearch坐标

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.16.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringData_ElasticSearch</artifactId>
    <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-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2)启动器配置文件

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

配置文件

spring:
 data:
  elasticsearch:
   cluster-name: my-elasticsearch
   cluster-nodes: 192.168.45.131:9300

3)编写实体Article

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;

@Document(indexName = "blog4",type="article")
public class Article {
    @Id
    @Field(type = FieldType.Long, store = true)
    private long id;
    @Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
    private String title;
    @Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
    private String content;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "Article{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

4)编写Dao
方法命名规则查询的基本语法findBy + 属性 + 关键词 + 连接符

关键字

命名规则

解释

示例

and

findByField1AndField2

根据Field1和Field2获得数据

findByTitleAndContent

or

findByField1OrField2

根据Field1或Field2获得数据

findByTitleOrContent

is

findByField

根据Field获得数据

findByTitle

not

findByFieldNot

根据Field获得补集数据

findByTitleNot

between

findByFieldBetween

获得指定范围的数据

findByPriceBetween

lessThanEqual

findByFieldLessThan

获得小于等于指定值的数据

findByPriceLessThan

import com.da.domain.Article;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import org.springframework.data.domain.Pageable;
import java.util.List;

public interface ArticelRepository extends ElasticsearchRepository<Article,Long> {
public List<Article> findByTitle(String title);
public List<Article> findByTitleOrContent(String title,String content);
public List<Article> findAllByContent(String content, Pageable pageable);
}

6)创建测试类

import com.da.dao.ArticelRepository;
import com.da.domain.Article;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;
import java.util.Optional;

@RunWith(SpringRunner.class)
@SpringBootTest
public class Test {
    @Autowired
    ArticelRepository articelRepository;
    @Autowired
    ElasticsearchTemplate template;

    @org.junit.Test
    public void createIndex() {
        //创建索引并配置映射关系
        template.createIndex(Article.class);
        //配置映射关系
        // template.putMapping(Article.class);
    }

    @org.junit.Test
    public void addDocument() {

        for (int i = 11; i < 20; i++) {
            Article article = new Article();
            article.setId(i);
            article.setTitle("女护士路遇昏迷男子跪地抢救:救人是职责更是本能" + i);
            article.setContent("这是一个美丽的女护士妹妹" + i);
            //把文档写入索引库
            articelRepository.save(article);
        }
    }

    @org.junit.Test
    public void deleteDocumentById() {
        //删除一个
        //articelRepository.deleteById(1L);
        //删除全部
        articelRepository.deleteAll();
    }

    @org.junit.Test
    public void findAll() {
        Iterable<Article> all = articelRepository.findAll();
        all.forEach(a -> System.out.println(a));
    }

    @org.junit.Test
    public void findFindById() {
        Optional<Article> byId = articelRepository.findById(14L);
        Article article = byId.get();
        System.out.println(article);

    }

    @org.junit.Test
    public void findByTitle() {
        List<Article> s = articelRepository.findByTitle("女护士");
        s.stream().forEach(a -> System.out.println(a));

    }

    @org.junit.Test
    public void findByContent() {
        Pageable pageable = PageRequest.of(1, 5);
        System.out.println("------------------");
        List<Article> a = articelRepository.findAllByContent("美丽", pageable);
        a.forEach(a1 -> System.out.println(a1));
    }

    @org.junit.Test
    public void testNativeSearchQuery() {
//创建一个查询对象
        NativeSearchQuery query = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.queryStringQuery("女护士")
                        .defaultField("title")).withPageable(PageRequest.of(0, 15))
                .build();
        //执行查询
        List<Article> list = template.queryForList(query, Article.class);
        list.forEach(a -> System.out.println(a));

    }

}

3. 聚合查询

实体类

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;

@Document(indexName = "blog4",   type = "car")
public class Car {
    @Id
    @Field(type = FieldType.Long, store = true)
    private Long id;
    @Field(type = FieldType.Text, store = true, analyzer = "ik_smart")
    private String name;
    @Field(type = FieldType.Text, store = true, analyzer = "ik_smart", fielddata
            = true)
    private String color;
    @Field(type = FieldType.Text, store = true, analyzer = "ik_smart", fielddata
            = true)
    private String brand;
    @Field(type = FieldType.Double, store = true)
    private Double price;
public Car(){}
    public Car(Long id, String name, String color, String brand, Double price) {
        this.id = id;
        this.name = name;
        this.color = color;
        this.brand = brand;
        this.price = price;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    @Override
    public String toString() {
        return "Car{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", color='" + color + '\'' +
                ", brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
}

Dao

import com.da.domain.Car;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface CarRepository extends ElasticsearchRepository<Car,Long> {

}

初始化索引库

import com.da.dao.CarRepository;
import com.da.domain.Car;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg;
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.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class CarTest {
    @Autowired
    CarRepository carDao;

    @Test
    public void saveCar(){
        carDao.save(new Car(1l, "比亚迪A1", "红色", "比亚迪", 50000d));
        carDao.save(new Car(2l, "比亚迪A2", "白色", "比亚迪", 70000d));
        carDao.save(new Car(3l, "比亚迪A3", "白色", "比亚迪", 80000d));
        carDao.save(new Car(4l, "比亚迪A4", "红色", "比亚迪", 60000d));
        carDao.save(new Car(5l, "比亚迪A5", "红色", "比亚迪", 90000d));
        carDao.save(new Car(6l, "宝马A1", "红色", "宝马", 10000d));
        carDao.save(new Car(7l, "宝马A2", "黑色", "宝马", 20000d));
        carDao.save(new Car(8l, "宝马A3", "黑色", "宝马", 30000d));
        carDao.save(new Car(9l, "宝马A4", "红色", "宝马", 40000d));
        carDao.save(new Car(10l, "宝马A5", "红色", "宝马", 50000d));
        carDao.save(new Car(11l, "奔驰A1", "红色", "奔驰", 10000d));
        carDao.save(new Car(12l, "奔驰A2", "黑色", "奔驰", 20000d));
        carDao.save(new Car(13l, "奔驰A3", "黑色", "奔驰", 30000d));
        carDao.save(new Car(14l, "奔驰A4", "红色", "奔驰", 40000d));
        carDao.save(new Car(15l, "奔驰A5", "红色", "奔驰", 50000d));

    }
   }

划分桶

GET /car_index/car/_search
{
"query": {
"bool": {
"should": [
{
"match_all": {}
}
]
}
},
"aggs": {
"group_by_bland": {
"terms": {
"field": "color"
}
}
}
}

桶内度量

GET /car_index/car/_search
{
"query": {
"bool": {
"should": [
{
"match_all": {}
}
]
}
},
"aggs": {
"group_by_bland": {
"terms": {
"field": "color"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}

Spring Data ElasticSearch代码实现

1.划分桶

@Test
    public void testQuerySelfAggs(){
        //查询条件的构造器
        NativeSearchQueryBuilder query=new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchAllQuery());
        //排除所有的字段查询
        query.withSourceFilter(new FetchSourceFilter(new String[]{},null));

        //添加聚合条件
        query.addAggregation(AggregationBuilders.terms("group_by_color").field("color"));
        //执行查询,把查询结果直接转为聚合page
        AggregatedPage<Car> a=(AggregatedPage<Car>)carDao.search(query.build());
        //从所有的聚合中获取对应名称的聚合
        StringTerms terms=(StringTerms) a.getAggregation("group_by_color");
        //从聚合的结果中获取所有的桶信息
        List<StringTerms.Bucket> buckets = terms.getBuckets();
          for(StringTerms.Bucket bucket:buckets){
              String keyAsString = bucket.getKeyAsString();
              long docCount = bucket.getDocCount();
              System.out.println("color:"+keyAsString+"   ======>"+"总数:"+docCount);
          }

    }

2.桶内度量

@Test
    public void testQuerySelfSubAggs(){
        //查询条件的构造器
        NativeSearchQueryBuilder query=new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery());
        //排除所有的字段查询
        query.withSourceFilter(new FetchSourceFilter(new String[]{},null));
        //添加聚合条件
        query.addAggregation(AggregationBuilders.terms("group_by_color").field("color")
                .subAggregation(AggregationBuilders.avg("avg_price").field("price")));
        //执行查询,把查询结果直接转为聚合page
        AggregatedPage<Car> a=(AggregatedPage<Car>) carDao.search(query.build());
        //从所有的聚合中获取对应名称的聚合
        StringTerms t=(StringTerms)a.getAggregation("group_by_color");
        //从聚合的结果中获取所有的桶信息
        List<StringTerms.Bucket> buckets = t.getBuckets();
        for(StringTerms.Bucket bucket:buckets){
            String keyAsString = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            InternalAvg avg_price =(InternalAvg)bucket.getAggregations().asMap().get("avg_price");
            System.out.println("color:"+keyAsString+"=====>"+"数量:"+docCount+"   ====>avg_price"+avg_price);
        }

    }