ES-Spring JPA操作

ElasticSearch在JPA与原生操作接口的对应关系

关键字 例子 Elasticsearch查询语句
And findByNameAndPrice {“bool” : {“must” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}}
Or findByNameOrPrice {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}}
Is findByName {“bool” : {“must” : {“field” : {“name” : “?”}}}}
Not findByNameNot {“bool” : {“must_not” : {“field” : {“name” : “?”}}}}
Between findByPriceBetween {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : ?,“include_lower” : true,“include_upper” : true}}}}}
LessThanEqual findByPriceLessThan {“bool” : {“must” : {“range” : {“price” : {“from” : null,”to” : ?,”include_lower” : true,”include_upper” : true}}}}}
GreaterThanEqual findByPriceGreaterThan {“bool” : {“must” : {“range” : {“price” : {“from” : ?,”to” : null,”include_lower” : true,”include_upper” : true}}}}}
Before findByPriceBefore {“bool” : {“must” : {“range” : {“price” : {“from” : null,”to” : ?,”include_lower” : true,”include_upper” : true}}}}}
After findByPriceAfter {“bool” : {“must” : {“range” : {“price” : {“from” : ?,”to” : null,”include_lower” : true,”include_upper” : true}}}}}
Like findByNameLike {“bool” : {“must” : {“field” : {“name” : {“query” : “?*”,”analyze_wildcard” : true}}}}}
StartingWith findByNameStartingWith {“bool” : {“must” : {“field” : {“name” : {“query” : “?*”,”analyze_wildcard” : true}}}}}
EndingWith findByNameEndingWith {“bool” : {“must” : {“field” : {“name” : {“query” : “*?”,”analyze_wildcard” : true}}}}}
Contains/Containing findByNameContaining {“bool” : {“must” : {“field” : {“name” : {“query” : “?”,”analyze_wildcard” : true}}}}}
In findByNameIn(Collectionnames) {“bool” : {“must” : {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“name” : “?”}} ]}}}}
NotIn findByNameNotIn(Collectionnames) {“bool” : {“must_not” : {“bool” : {“should” : {“field” : {“name” : “?”}}}}}}
Near findByStoreNear 暂不支持
True findByAvailableTrue {“bool” : {“must” : {“field” : {“available” : true}}}}
False findByAvailableFalse {“bool” : {“must” : {“field” : {“available” : false}}}}
OrderBy findByAvailableTrueOrderByNameDesc {“sort” : [{ “name” : {“order” : “desc”} }],”bool” : {“must” : {“field” : {“available” : true}}}}

实体声明

  • 1、类上注解:@Document (相当于Hibernate实体的@Entity/@Table)
类型 属性名 默认值 说明
String indexName 索引库的名称
String type “” 类型
short shards 5 默认分区数
short replica 1 每个分区默认的备份数
String refreshInterval “1s” 刷新间隔
String indexStoreType “fs” 索引文件存储类型
  • 2、主键注解:@Id (相当于Hibernate实体的主键@Id注解) 只是一个标识,并没有属性。
  • 3、属性注解 @Field (相当于Hibernate实体的@Column注解)

@Field默认是可以不加的,默认所有属性都会添加到ES中。

类型 属性名 默认值 说明
FileType type FieldType.Auto 自动检测属性的类型
FileType index FieldIndex.analyzed 默认情况下分词
boolean store false 默认情况下不存储原文
String searchAnalyzer “” 指定字段搜索时使用的分词器
String indexAnalyzer “” 指定字段建立索引时指定的分词器
String[] ignoreFields {} 如果某个字段需要被忽略
实例

实体:

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
/**
* index
* 索引,相当于Mysql中的一个库,例如有一个叫『jd』的库,那么里面可以建立很多表,存储不同类型的数据,而表在ES中就是type。
* type
* 类型,相当于Mysql中的一张表,存储json类型的数据
* document
* 文档,一个文档相当于Mysql一行的数据
* shards
* 分片,通俗理解,就是数据分成几块区域来存储,可以理解为mysql中的分库分表(不太恰当)
* replicas
* 备份,就是分片的备份数,相当于mysql的备份库
*
*/
@Data
@Document(indexName = "account", type = "_doc", refreshInterval = "0s")
public class Account {

@Id
private Integer id;
private String userName;
private String realName;
private String password;
private Integer age;
//这三个注解是为了前台序列化java8 LocalDateTime使用的,需要引入jsr310的包才可以使用
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime birth;

}

DAO:

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
* @Description TODO
* @Author huyunshun 2019/8/16
*/
public interface AccountRepository extends ElasticsearchRepository<Account, Integer> {

/**
* 查询账户名为username的账户
* @param userName
* @return
*/
List<Account> findByUserName(String userName);

/**
* 查询账户名为username并且真实姓名为realname的账户
* @param userName
* @param realName
*/
List<Account> findByUserNameAndRealName(String userName, String realName);

/**
* 查询账户名为userName或者姓名为realName的账户
*/
List<Account> findByUserNameOrRealName(String userName, String realName);

/**
* 查询账户名不是username的所有账户
* @param userName
* @return
*/
List<Account> findByUserNameNot(String userName);


/**
* 查询年龄段为ageFrom到ageTo的账户
* @param ageFrom
* @param ageTo
* @return
*/
List<Account> findByAgeBetween(Integer ageFrom, Integer ageTo);

/**
* 查询生日小于birth的账户
*/
List<Account> findByBirthLessThan(LocalDateTime birth);


/**
* 查询生日段大于birthFrom的账户
* @param birth
* @return
*/
List<Account> findByBirthGreaterThan(LocalDateTime birth);

/**
* 查询年龄小于或等于age的账户
*/
List<Account> findByAgeBefore(Integer age);

/**
* 查询年龄大于或等于age的账户
* @param age
* @return
*/
List<Account> findByAgeAfter(Integer age);

/**
* 账户名模糊查询
* @param userName
* @return
*/
List<Account> findByUserNameLike(String userName);


/**
* 查询以start开头的账户
* @param start
* @return
*/
List<Account> findByUserNameStartingWith(String start);

/**
* 查询以end结尾的账户
* @return
*/
List<Account> findByUserNameEndingWith(String end);

/**
* 查询账户名包含word的账户
* @param word
* @return
*/
List<Account> findByUserNameContaining(String word);

/**
* 查询名字属于usernames中的账户
* @param userNames
* @return
*/
List<Account> findByUserNameIn(Collection<String> userNames);

/**
* 查询名字不属于userNames中的账户
* @param userNames
* @return
*/
List<Account> findByUserNameNotIn(Collection<String> userNames);

/**
*查询年龄小于age,姓名以start开头,id大于id的账户,并且按照年龄倒序
* @return
*/
List<Account> findByAgeBeforeAndUserNameStartingWithAndIdGreaterThanOrderByAgeDesc(Integer age, String start, Integer id);

/** 写ES的查询语句
* 查询名字为name的账户 分页
* @param name
* @param pageable
* @return
*/
@Query("{\"bool\" : {\"must\" : {\"field\" : {\"name\" : \"?0\"}}}}")
Page<Account> findByName(String name, Pageable pageable);

}

Hibernate中criteria的方式进行查询

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
public Page<Account> getAccounts() {
//创建builder
BoolQueryBuilder builder = QueryBuilders.boolQuery();
//设置模糊搜索,真实姓名中包含胡的用户
builder.must(QueryBuilders.fuzzyQuery("realName", "胡"));
//设置用户名为huing
builder.must(new QueryStringQueryBuilder("huing").field("userName"));

//排序
FieldSortBuilder sort = SortBuilders.fieldSort("age").order(SortOrder.DESC);

//设置分页
PageRequest page = new PageRequest(0, 2);

//构建查询
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
//将搜索条件设置到构建中
nativeSearchQueryBuilder.withQuery(builder);
//将分页设置到构建中
nativeSearchQueryBuilder.withPageable(page);
//将排序设置到构建中
nativeSearchQueryBuilder.withSort(sort);
//生产NativeSearchQuery
NativeSearchQuery query = nativeSearchQueryBuilder.build();

//执行,返回包装结果的分页
Page<Account> resutlList = accountRepository.search(query);

return resutlList;
}
术语
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
55
Analysis(分析)
​ 分析的过程就是将全文(full text)转换成 术语/分词(terms)。 这取决于使用那个分析器,这些短语:“FOO BAR”, “Foo-Bar”, “foo,bar”,可能会拆分成” foo”和“bar”。这些拆分后的词实际上是存储在索引中。一个完整的文本查询(而不是一个分词查询) “FoO:bAR”,将被分解成 “foo”,“bar”,来匹配存储在索引中的分词。正是这一过程的分析(包括在索引时间和搜索时间),允许elasticsearch执行全文查询。

Cluster (集群)
​ 一个集群包含一个或多个分配了相同的集群名称的节点。每个集群都有一个主节点是集群自动选择产生,并且可以决定如果当前主节点失败,哪些可以替换。

Document(文档)
​ 文档是存储在elasticsearch中的一个JSON文件。这是相当与关系数据库中表的一行数据。每个文档被存储在索引中,并具有一个类型和一个id。一个文档是一个JSON对象(也被称为在其他语言中的 hash / hashmap / associative array(关联数组)),其中包含零个或多个字段 或者 键值对。原始JSON文档将被存储在索引的_source字段,在获得(getting)或者 搜索(searching)默认的返回时,得到或搜索文档。

Id(标识)
​ 每个文档ID标识了一个文档。一个文档的索引/类型/ ID必须是唯一的。如果没有提供ID,将是自动生成。(还可以看到路由)

Field(字段)
​ 文档中包含的一组字段或键值对。字段的值可以是一个简单的(标量)值(如字符串,整数,日期),或者一个嵌套的结构就像一个数组或对象。一个字段就是类似关系数据库表中的一列。映射的每个字段有一个字段的类型“type”(不要与文档类型混淆),表示那种类型的数据可以存储在该字段里,如:整数,字符串,对象。映射还允许你定义(除其他事项外)一个字段的值如何进行分析。

Index(索引)
​ 索引就是像关系数据库中的“数据库”。通过映射可以定义成多种类型。索引是一个逻辑命名空间映射到一个或多个主要的分片,可以有零个或多个副本分片。

Mapping(映射)
​ 映射是像关系数据库中的”模式定义“。每个索引都有一个映射,它定义了每个索引的类型,再加上一些索引范围的设置。映射可以被明确地定义,或者在一个文档被索引的时候自动生成。

Node(节点)
​ 节点是属于elasticsearch群集的运行实例。测试的时候,在一台服务器可以启动多个节点,但通常情况下应该在一台服务器运行一个节点。在启动时,节点将使用单播(或组播,但是必须指定)来发现使用相同的群集名称的群集,并会尝试加入该群集。

Primary shard(主分片)
​ 每个文档都存储在一个主要分片上。当你索引一个文档时,索引首先生成在主分片上,然后才到主分片的所有副本上。默认情况下,索引有5个主分片。您可以指定更多或更少的主分片来适应索引可以处理的文档数。一旦创建了索引,就不能改变索引中主分片的数量。

Replica shard(副本分片)
​ 每个主分片可以有零个或多个副本。副本是主分片的一个拷贝,有两个作用:

​ 1、故障转移:如果主分片有问题,副本分片可以提升为主分片;

​ 2、提高性能:获取和搜索请求可以处理主分片或副本分片。

​ 默认情况下,每个主分片有一个副本,不过索引的副本数量可以动态地改变。在同一个节点上,一个副本分片将永远不会和其主分片一起运行。

Routing(路由)
​ 当你索引一个文档,它是存储在一个主分片里。这分片的选择是通过哈希的路由值。默认情况下,路由值来自文档的ID;如果该文档指定了父文档,则使用父文档的ID(以确保这个子文档和父文件都存储在相同的分片上)。这个路由值可以在索引的时候,通过指定数值或者配置字段映射来覆盖。

Shard(分片)
​ 一个分片是一个单一的Lucene的实例。这是一个低级别的通过ElasticSearch自动管理的“工作者”单元。索引是一个逻辑命名空间指向主分片和副本分片。索引的主分片和副本分片的数量需要明确的指定。然而你的代码应该只处理一个索引。Elasticsearch分配集群中所有节点的分片。在节点出现故障或增加新节点的时候,可以自动的将一个节点上的分片移动到另一个节点上。

Source field(源字段)
​ 默认情况下,你的JSON文档将被索引存储在_source字段里面,所有的get(获取)和search(搜索)请求将返回的该字段。这将允许你直接从搜索结果中访问到源数据,而不需要再次发起请求检索。

​ 注:索引将返回完整的的JSON字符串给你,即使它包含无效的JSON。此字段里的内容不表示任何该对象里面的数据如何被索引。

Term(术语)
​ 在elasticsearch里,术语(term)是一个被索引的精确值。术语 foo, Foo,FOO 是不想等的。术语(即精确值)可以使用“term”查询接口来查询。

Text(文本)
​ 文本(或全文)是普通非结构化的文本,如本段。默认情况下,文本将被分析成术语,术语才是实际存储在索引中。文本字段在索引时需要进行分析,以便全文搜索,全文查询的关键字在搜索时,必须分析产生(搜索)与索引时相同的术语。

Type(类型)
​ Type是相当于关系数据库中的“表”。每种类型都有一列字段,用来定义文档的类型。映射定义了对在文档中的每个字段如何进行分析。
QueryBuilders构建查询

QueryBuilder如:MatchAllQueryBuilder.class,FuzzyQueryBuilder.class等,它们是具体的查询实现类,它们是QueryBuilders抽象类的子类并实现了BoostableQueryBuilder<>接口。

而QueryBuilders的主要作用就是提供了静态方法去创建一个子类的对象,方便QueryBuilder的构建与管理。

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
匹配所有文档的查询。
matchAllQuery()
为提供的字段名和文本创建类型为“BOOLEAN”的匹配查询。(解释过来就是单个匹配,可以模糊匹配)
matchQuery(String name, Object text) //name 字段值 ,text 查询文本(不支持通配符)
为提供的字段名和文本创建一个通用查询。
commonTermsQuery(String name, Object text)
为提供的字段名和文本创建类型为“BOOLEAN”的匹配查询。
multiMatchQuery(Object text, String... fieldNames)
为提供的字段名和文本创建一个文本查询,并输入“短句”。
matchPhraseQuery(String name, Object text)
为提供的字段名和文本创建一个与类型“PHRASE_PREFIX”匹配的查询。
matchPhrasePrefixQuery(String name, Object text)
匹配包含术语的文档的查询。
termQuery(String name, Object value)
使用模糊查询匹配文档的查询
fuzzyQuery(String name, Object value)
与包含指定前缀的术语的文档相匹配的查询。
prefixQuery(String name, String prefix)
在一定范围内匹配文档的查询。
rangeQuery(String name)
实现通配符搜索查询。支持的通配符是*,它匹配任何字符序列(包括空字符),而?它匹配任何单个字符。注意,这个查询可能很慢,因为它需要遍历许多项。为了防止异常缓慢的通配符查询,通配符项不应该以一个通配符*或?开头。

wildcardQuery(String name, String query) //query 通配符查询字符串
将包含术语的文档与指定的正则表达式匹配的查询
regexpQuery(String name, String regexp) //regexp的正则表达式
解析查询字符串并运行它的查询。有两种模式。第一,当没有字段添加(使用QueryStringQueryBuilder.field(字符串),将运行查询一次,非前缀字段将使用QueryStringQueryBuilder.defaultField(字符串)。第二,当一个或多个字段添加(使用QueryStringQueryBuilder.field(String)),将运行提供的解析查询字段,并结合使用DisMax或普通的布尔查询(参见QueryStringQueryBuilder.useDisMax(布尔))。

queryStringQuery(String queryString)
类似于query_string查询的查询,但不会为任何奇怪的字符串语法抛出异常。
simpleQueryStringQuery(String queryString)
可以使用BoostingQuery类来有效地降级与给定查询匹配的结果。
boostingQuery()
匹配与其他查询的布尔组合匹配的文档的查询
boolQuery()
创建一个可用于实现MultiTermQueryBuilder的子查询的SpanQueryBuilder。
spanMultiTermQueryBuilder(MultiTermQueryBuilder multiTermQueryBuilder)
允许定义自定义得分函数的查询。
functionScoreQuery(QueryBuilder queryBuilder, ScoreFunctionBuilder function)
更像这样的查询,查找“like”提供的文档,例如提供的MoreLikeThisQueryBuilder.likeText(String),它是针对查询所构造的字段进行检查的

moreLikeThisQuery(String... fields)
构造一个新的非计分子查询,包含子类型和要在子文档上运行的查询。这个查询的结果是这些子文档匹配的父文档。
hasChildQuery(String type, QueryBuilder query)
构造一个新的非评分父查询,父类型和在父文档上运行的查询。这个查询的结果是父文档匹配的子文档。
hasParentQuery(String type, QueryBuilder query)
基于对其中任何一个项进行匹配的若干项的字段文件
termsQuery(String name, String... values)
一个查询构建器,它允许构建给定JSON字符串或二进制数据作为输入的查询。当您希望使用Java Builder API,但仍然需要将JSON查询字符串与其他查询构建器结合时,这是非常有用的。

wrapperQuery(String source)
这些都是官方提供的注释,有些晦涩难懂,还有很多,我这里不一一列举。

我们使用上述静态方法只是为了方便创建QueryBuilder系列对象。

条件查询

调用QueryBuilders 的静态方法创建完具体的QueryBuilder对象之后,传入.withQuery(QueryBuilder)方法就可以实现的自定义查询。

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
1.完全匹配
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("字段名", "查询文本");
2.短语匹配
比如你要查询短语Love You,当你使用QueryBuilders.matchQuery("name", "Love You")时,可能会查询到Love And You这样分开的结果,这时候我们指定为短语进行查询:

QueryBuilder queryBuilder = QueryBuilders.matchPhraseQuery("name", "Love You");
3.模糊匹配
MoreLikeThisQueryBuilder queryBuilder = QueryBuilders
.moreLikeThisQuery("name")// 要匹配的字段, 不填默认_all
.like("西游")// 匹配的文本
.minTermFreq(1);// 文本最少出现的次数(默认是2,我们设为1)
我们再从源码去看其它方法

添加一些文本以查找“类似”的文档
addLikeText(String... likeTexts)
查找类似文档
like(Item... likeItems)
设置不从其中选择(比如我们调用.like("西游").unlike("西游记")这样会导致啥也查不到)
unlike(String... unlikeTexts)
添加一些文本以查找与此不同的文档
addUnlikeText(String... unlikeTexts)
设置将包含在任何生成查询中的查询条件的最大数量。默认25
maxQueryTerms(int maxQueryTerms)
设置单词被忽略的频率,默认5,小于将不会被发现
minDocFreq(int minDocFreq)
设置单词仍然出现的最大频率。单词出现更多的文档将被忽略。默认为无限
maxDocFreq(int maxDocFreq)
设置将被忽略的单词的最小单词长度,默认0
minWordLength(int minWordLength)
设置将被忽略的单词的最大单词长度,默认无限
maxWordLength(int maxWordLength)
设置停止词,匹配时会忽略停止词
stopWords(String... stopWords)
设置词语权重,默认是1
boostTerms(float boostTerms)
查询权重(默认1)
boost(float boost)
设置不从其中选择术语的文本(文档Item)
ignoreLike(String... likeText)
4.组合查询
QueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("name", "西游")) //AND
.must(QueryBuilders.wildcardQuery("name", "西游?")) //Not
.should(QueryBuilders.matchQuery("name", "西游记")); //Or
5.包裹查询
高于设定分数, 不计算相关性

QueryBuilder queryBuilder = QueryBuilders
.constantScoreQuery(QueryBuilders.matchQuery("name", "西游记"))
.boost(2.0f);
6.范围查询
查询id值在20-50之间的

QueryBuilder queryBuilder = QueryBuilders.rangeQuery("id")
.from("20")
.to("50")
.includeLower(true) // 包含上界
.includeUpper(true); // 包含下届
7.通配符查询
通配符查询, 支持 * 匹配任何字符序列, 包括空

避免* 开始, 会检索大量内容造成效率缓慢

单个字符用?

QueryBuilder queryBuilder = QueryBuilders.wildcardQuery("user", "ki*hy");
8.过滤查询
QueryBuilders.constantScoreQuery(FilterBuilders.termQuery("name", "kimchy")).boost(2.0f);