很早很早以前就听说了ElasticSearch,知道了它对大量数据的强悍搜索功能,当时就想以后一定要用一下这个东西,把它加到我自己的项目中。兜兜转转终于到了使用的这天,虽说有点大材小用,但还是在我的博客项目中使用了它。
ElasticSearch | MYSQL |
---|---|
索引库(indices) | 数据库(databases) |
类型(type) | 表(table) |
文档(document) | 行(row) |
字段(field) | 列(column) |
需要注意的是安装ElasticSearch时要注意自己springboot引入的springdata引入的ElasticSearch版本
我的版本是2.4.6,所以我下载镜像时也是下载的相应的版本。
在Docker仓库查看对应版本的镜像,然后使用下面的命令下载
docker pull elasticsearch:2.4.6
然后启动镜像,指定运行内存最大与最小都为256m,后台运行,端口映射
docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name ES01 5e9d896dc62c
properties文件(cluster-name就是上面的cluster_name,一般默认是elasticsearch)
spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.cluster-nodes=118.25.236.51:9300
Image类
@Data
@Document(indexName = "blog",type = "image")
public class Image {
private int id;
private String url;
private int type;
@Field(type = FieldType.String,analyzer = "ik_max_word")
private String tag;
@Field(type = FieldType.String,analyzer = "ik_max_word")
private String description;
}
Spring Data通过注解来声明字段的映射属性,有下面的三个注解:
@Document
作用在类,标记实体类为文档对象,一般有两个属性
@Id
作用在成员变量,标记一个字段作为id主键@Field
作用在成员变量,标记为文档的字段,并指定字段映射属性:
ik_max_word
即使用ik分词器(关于IK分词器将在以后介绍)ElasticsearchRepository接口
@NoRepositoryBean
public interface ElasticsearchRepository<T, ID extends Serializable> extends ElasticsearchCrudRepository<T, ID> {
<S extends T> S index(S var1);
Iterable<T> search(QueryBuilder var1);
Page<T> search(QueryBuilder var1, Pageable var2);
Page<T> search(SearchQuery var1);
Page<T> searchSimilar(T var1, String[] var2, Pageable var3);
void refresh();
Class<T> getEntityClass();
}
继承接口(同JpaRepository等接口相同,继承接口就已经有实现的操作数据的方法)
public interface ImageRepository extends ElasticsearchRepository<Image,Integer> {
}
@Test
public void addImage(){
Image image = new Image();
image.setId(4);
image.setUrl("444");
image.setTag("tag");
image.setType(1);
image.setDescription("aaaaaaa");
imageRepository.save(image);
}
@Test
public void deleteImage(){
imageRepository.delete(4);
}
@Test
public void update(){
Image image = new Image();
image.setId(4);//注意id应该与要更改的数据的相同
image.setUrl("333");
image.setTag("tag1");
image.setType(1);
image.setDescription("aaaaaaa");
imageRepository.save(image);
}
Keyword | Sample | Elasticsearch Query String |
---|---|---|
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(Collection |
{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}} |
NotIn |
findByNameNotIn(Collection |
{"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}} |
Near |
findByStoreNear |
Not Supported Yet ! |
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}}}} |
match查询
无论你在任何字段上进行的是全文搜索还是精确查询,match
查询是你可用的标准查询。
如果你在一个全文字段上使用 match
查询,在执行查询前,它将用正确的分析器去分析查询字符串:如果在一个精确值的字段上使用它, 例如数字、日期、布尔或者一个 not_analyzed
字符串字段,那么它将会精确匹配给定的值。(查询前可以分词,可以不分词,看类型)
@Test
public void query(){
String tag = "武器";
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
//查询tag域中存在"武器"的数据
queryBuilder.withQuery(QueryBuilders.matchQuery("tag", tag));
Page<Image> search = imageRepository.search(queryBuilder.build());
for (Image image : search) {
System.out.println(image);
}
}
term查询
term
查询被用于精确值 匹配,这些精确值可能是数字、时间、布尔或者那些 not_analyzed
的字符串,term
查询对于输入的文本不 分析 ,所以它将给定的值进行精确查询。
更多的组合语法请参考Elasticsearch查询的官方文档
@Test
public void query() {
String tag = "武器";
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
queryBuilder.withQuery(QueryBuilders.termQuery("tag", tag));
Page<Image> search = imageRepository.search(queryBuilder.build());
for (Image image : search) {
System.out.println(image);
}
}
可以用 bool
查询来实现组合查询。这种查询将多查询组合在一起,成为用户自己想要的布尔查询。它接收以下参数:
must
文档 必须 匹配这些条件才能被包含进来。
must_not
文档 必须不 匹配这些条件才能被包含进来。
should
如果满足这些语句中的任意语句,将增加 _score
,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
filter
必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
public List<Image> combineQuery(String search) {
//组合查询BoolQueryBuilder
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//精确匹配tag,不分词
//should相当于or,下面语句的意思是,找到tag域 或 description域中存在search的文档
boolQueryBuilder.should(QueryBuilders.termQuery("tag", search))
//两种条件会出现不同结果,具体看后面分析
// .should(QueryBuilders.termQuery("description",search));
.should(QueryBuilders.matchQuery("description", search));
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
//根据id域升序排列查询结果
NativeSearchQuery searchQuery = nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.ASC)).withQuery(boolQueryBuilder).build();
Page<Image> search1 = imageRepository.search(searchQuery);
List<Image> returnImages = new ArrayList<>();
for (Image image : search1) {
returnImages.add(image);
}
return returnImages;
}
前面已经讲过match
与term
的区别,现在直观的来看一下
description使用matchQuery
description使用matchQuery
description使用term
当使用term时,查询字段不会分词,直接是武器气势
精确查询,但是索引中却不存在这个索引,真正的索引如下:
所以当使用term
进行精确查询时,返回结果就为空。