参考https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.mapping
一定要注意版本的问题:版本不一致,会带来很多问题:
Spring Data Elasticsearch | Elasticsearch | Spring Boot |
---|---|---|
3.2.x | 6.8.4 | 2.2.x |
3.1.x | 6.2.2 | 2.1.x |
3.0.x | 5.5.0 | 2.0.x |
2.1.x | 2.4.0 | 1.5.x |
首先导入关键dependency
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
# cluster-name不要写错
spring.data.elasticsearch.cluster-name=elasticsearch
# Java调用时端口是9300,restfu调用端口是9200,注意区分
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
spring.data.elasticsearch.properties.transport.tcp.connect_timeout=120s
然后编写实体类型type
/**
* @author Honyelchak
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
// 若ES中没有指定的索引,会自动创建。
// shards 分片数 replicas 副本数 refreshInterval 刷新间隔
@Document(indexName = "hello_world", type="user", shards = 2, replicas = 3)
public class Item implements Serializable {
@Id
private String id;
private String userName;
@Field(type = FieldType.Keyword)
private String passWord;
@Field(type = FieldType.Integer)
private Integer age;
// text 类型的会分词
@Field(type = FieldType.Text, analyzer = "")
private String comment;
}
@Document(indexName = "hello_world",type = "user",shards = 5,replicas = 1)
: 标注在实体类上,声明存储的索引和类型刚才,
indexName
: 索引名称type
:索引类型shards
:分片的数量,默认为·5replicas
:副本的数量,默认为1refreshInterval
: 刷新间隔,默认为1s
indexStoreType
:索引文件存储类型,默认为fs
@Field
标注在属性上,用来指定属性的类型。其中的属性如下:
analyzer
:指定分词器,es中默认使用的标准分词器,比如我们需要指定中文IK分词器,可以指定值为ik_max_word
type
: 指定该属性在es中的类型,其中的值是FileType
类型的值,比如FileType.Text
类型对应es中的text类型index
:指定该词是否需要索引,默认为truestore
:指定该属性内容是否存储在ES,默认为falsefielddata
:指定该属性能否进行排序,因为es中的text类型是不能进行排序(已经分词了)searchAnalyzer
: 指定搜索使用的分词器name
:字段名称,可以在ES文档中表示,若未设置,则使用Java字段名称。直接在 dao
接口继承 ElasticsearchRepository
直接在 Service/Controller
中引入 ElasticsearchTemplate
ElasticsearchRepository
Spring Data的中央接口是Repository
。
CrudRepository
规定对于正在管理的实体类复杂的CRUD功能。public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
Optional<T> findById(ID primaryKey);
Iterable<T> findAll();
long count();
void delete(T entity);
boolean existsById(ID primaryKey);
// … more functionality omitted.
}
ES中的PagingAndSortingRepository
接口 继承了CrudRepository
。简化了分页操作和排序操作:
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
而在SpringBoot项目中,常用的是ElasticsearchRepository
,他在ElasticsearchCrudRepository
的基础上补充了一些与查询相关的方法。
interface BookRepository extends Repository<Book, String> {
List<Book> findByNameAndPrice(String name, Integer price);
}
上面的方法名称将转换为以下Elasticsearch json查询,
{
"query": {
"bool" : {
"must" : [
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } },
{ "query_string" : { "query" : "?", "fields" : [ "price" ] } }
]
}
}
}
还可以用@Query
注解来自定义查询语句,例如:
@Query("{\n" +
" \"bool\": {\n" +
" \"must\": [\n" +
" {\n" +
" \"match\": {\n" +
" \"userName\": \"?0\"\n" +
" }\n" +
" }\n" +
" ]\n" +
" }\n" +
" }")
List<User> selectByUserName(String userName);
PS: 其中的?是占位符,0表示第一个参数
对应表:
关键词 | 样品 | Elasticsearch查询字符串 |
---|---|---|
And |
findByNameAndPrice |
{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }} |
Or |
findByNameOrPrice |
{ "query" : { "bool" : { "should" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }} |
Is |
findByName |
{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }} |
Not |
findByNameNot |
{ "query" : { "bool" : { "must_not" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }} |
Between |
findByPriceBetween |
{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }} |
LessThan |
findByPriceLessThan |
{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } } ] } }} |
LessThanEqual |
findByPriceLessThanEqual |
{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }} |
GreaterThan |
findByPriceGreaterThan |
{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } } ] } }} |
GreaterThanEqual |
findByPriceGreaterThan |
{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }} |
Before |
findByPriceBefore |
{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }} |
After |
findByPriceAfter |
{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }} |
Like |
findByNameLike |
{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }} |
StartingWith |
findByNameStartingWith |
{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }} |
EndingWith |
findByNameEndingWith |
{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }} |
Contains/Containing |
findByNameContaining |
{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }} |
In |
findByNameIn(Collectionnames) |
{ "query" : { "bool" : { "must" : [ {"bool" : {"must" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }} |
NotIn |
findByNameNotIn(Collectionnames) |
{ "query" : { "bool" : { "must" : [ {"bool" : {"must_not" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }} |
Near |
findByStoreNear |
Not Supported Yet ! |
True |
findByAvailableTrue |
{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }} |
False |
findByAvailableFalse |
{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "false", "fields" : [ "available" ] } } ] } }} |
OrderBy |
findByAvailableTrueOrderByNameDesc |
{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }, "sort":[{"name":{"order":"desc"}}] } |
ElasticsearchTemplate
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@GetMapping("createIndex")
public boolean createIndex(String indexName) {
return elasticsearchTemplate.createIndex(indexName);
}
@GetMapping("deleteIndex")
public boolean deleteIndex(String indexName) {
return elasticsearchTemplate.deleteIndex(indexName);
}
@GetMapping("indexIsExist")
public boolean indexIsExist(String indexName) {
return elasticsearchTemplate.indexExists(indexName);
}
@GetMapping("typeIsExist")
public boolean typeIsExist(String indexName, String type) {
return elasticsearchTemplate.typeExists(indexName, type);
}
@GetMapping("getMapping")
public Map getMapping(String indexName, String type) {
return elasticsearchTemplate.getMapping(indexName, type);
}
@GetMapping("getSetting")
public Map getSetting(String indexName) {
return elasticsearchTemplate.getSetting(indexName);
}
// 分词查询 :comment中包含中国的用户,并且以age倒序返回。
@Test
public void searchByCommentSortByAge() {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.should(QueryBuilders.termQuery("comment", "中国"));
FieldSortBuilder sortBuilder = SortBuilders.fieldSort("age").order(SortOrder.DESC);
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
nativeSearchQueryBuilder.withSort(sortBuilder);
NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
Page<Item> itemsPage = itemRepository.search(searchQuery);
if (itemsPage != null) {
List<Item> items = itemsPage.getContent();
items.forEach(System.out::println);
} else {
System.out.println("can not find that!");
}
}
// 查询userName为fanqi的用户,并且以age倒序返回。
@Test
public void searchByUserNameSortByAge() {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.should(QueryBuilders.termQuery("userName", "fanqi"));
FieldSortBuilder sortBuilder = SortBuilders.fieldSort("age").order(SortOrder.DESC);
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
nativeSearchQueryBuilder.withSort(sortBuilder);
NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
Page<Item> itemsPage = itemRepository.search(searchQuery);
if (itemsPage != null) {
List<Item> items = itemsPage.getContent();
items.forEach(System.out::println);
} else {
System.out.println("can not find that!");
}
}