一.认识ElasticSearch
1.为什么要使用ElasticSearch
虽然全文搜索领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。
但是,Lucene只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene的配置及使用非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。
实际项目中,我们建立一个网站或应用程序,并要添加搜索功能,令我们受打击的是:搜索工作是很难的。我们希望我们的搜索解决方案要快,我们希望有一个零配置和一个完全免费的搜索模式,我们希望能够简单地使用JSON/XML通过HTTP的索引数据,我们希望我们的搜索服务器始终可用,我们希望能够从一台开始并在需要扩容时方便地扩展到数百,我们要实时搜索,我们要简单的多租户,我们希望建立一个云的解决方案。
2.ElasticSearch(简称ES)
ES即为了解决原生Lucene使用的不足,优化Lucene的调用方式,并实现了高可用的分布式集群的搜索方案,其第一个版本于2010年2月出现在GitHub上并迅速成为最受欢迎的项目之一。
首先,ES的索引库管理支持依然是基于Apache Lucene(TM)的开源搜索引擎。
ES也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
二.ES安装及使用说明
1.安装ES
官方下载地址:https://www.elastic.co/downloads/elasticsearch
运行ES bin/elasticsearch.bat
![访问 http://localhost:9200/ 看到图中信息说明ES已经启动并且正常运行] (https://img-blog.csdnimg.cn/20200610233524529.png)
2.辅助管理工具Kibana5
Kibana5.2.2下载地址:https://www.elastic.co/downloads/kibana
启动Kibana5 : bin\kibana.bat
默认访问地址:http://localhost:5601
![ Discover:可视化查询分析器
Visualize:统计分析图表
Dashboard:自定义主面板(添加图表)
Timelion:Timelion是一个kibana时间序列展示组件(暂时不用)
Dev Tools :Console(同CURL/POSTER,操作ES代码工具,代码提示,很方便)
Management:管理索引库(index)、已保存的搜索和可视化结果(save objects)、设置 kibana 服务器属性。]
(https://img-blog.csdnimg.cn/20200610234459195.png)
3.ES相关概念理解
(1)Near Realtime(NRT):近实时,两个意思,从写入数据到数据可以被搜索到有一个小延迟(大概1秒);基于es执行搜索和分析可以达到秒级
(2)Index:索引库,包含一堆有相似结构的文档数据,比如可以有一个客户索引,商品分类索引,订单索引,索引有一个名称。一个index包含很多document,一个index就代表了一类类似的或者相同的document。比如说建立一个product index,商品索引,里面可能就存放了所有的商品数据,所有的商品document。
(3)Type:类型,每个索引里都可以有一个或多个type,type是index中的一个逻辑数据分类,一个type下的document,都有相同的field,比如博客系统,有一个索引,可以定义用户数据type,博客数据type,评论数据type。
(4)Document&field:文档,es中的最小数据单元,一个document可以是一条客户数据,一条商品分类数据,一条订单数据,通常用JSON数据结构表示,每个index下的type中,都可以去存储多个document。一个document里面有多个field,每个field就是一个数据字段。
(5)Cluster:集群,包含多个节点,每个节点属于哪个集群是通过一个配置(集群名称,默认是elasticsearch)来决定的,对于中小型应用来说,刚开始一个集群就一个节点很正常
(6)Node:节点,集群中的一个节点,节点也有一个名称(默认是随机分配的),节点名称很重要(在执行运维管理操作的时候),默认节点会去加入一个名称为“elasticsearch”的集群,如果直接启动一堆节点,那么它们会自动组成一个elasticsearch集群,当然一个节点也可以组成一个elasticsearch集群
(7)shard(ʃɑːrd,分片):单台机器无法存储大量数据,es可以将一个索引中的数据切分为多个shard,分布在多台服务器上存储。有了shard就可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升吞吐量和性能。每个shard都是一个lucene index。
(8)replica(ˈreplɪkə,复制品):任何一个服务器随时可能故障或宕机,此时shard可能就会丢失,因此可以为每个shard创建多个replica副本。replica可以在shard故障时提供备用服务,保证数据不丢失,多个replica还可以提升搜索操作的吞吐量和性能。primary shard(建立索引时一次设置,不能修改,默认5个),replica shard(随时修改数量,默认1个),默认每个索引10个shard,5个primary shard,5个replica shard,最小的高可用配置,是2台服务器。
三.ES文档操作
1.什么是ES中的文档
document --存放数据。在ES里面 通过index索引库 type 类型(表) 行({id:name:}) 列 id /name。ES是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在ES中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。
_index:索引库,类似于关系型数据库里的“数据库”—它是我们存储和索引关联数据的地方。
_type:在应用中,我们使用对象表示一些“事物”,例如一个用户、一篇博客、一个评论,或者一封邮件。可以是大写或小写,不能包含下划线或逗号。我们将使用 employee 做为类型名。
_id:与 _index 和 _type 组合时,就可以在ELasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义 _id ,也可以让Elasticsearch帮你自动生成。
另外还包括:
_uid文档唯一标识(_type#_id)
_source:文档原始数据
_all:所有字段的连接字符串
2.文档的增删改
创建索引文档
①使用自己的ID创建
PUT { index} / { type} / { id}
{
"field" : "value" ,
. . .
}
②ES内置ID创建:
POST { index} / { type} /
{
"field" : "value" ,
. . .
}
①②ES响应内容:
{
"_index" : "itsource" ,
"_type" : "employee" ,
"_id" : xxxxxx,
"_version" : 1 ,
"created" : true
}
更新整个文档
post { index} / { type} / { id}
{
Id: 1 ,
Name: xx
}
在响应中可以看到Elasticsearch把 _version增加
局部更新文档
POST itsource/ employee/ 123 / _update
{
“doc”: {
"email" : "nixianhua@itsource.cn" ,
"salary" : 1000
}
}
这个过程如下:1. 从旧文档中检索JSON2. 修改它3. 删除旧文档4. 索引新文档
脚本更新文档
POST itsource/ emploee/ 123 / _update
{
"script" : "ctx._source.age += 5"
}
删除文档
DELETE { index} / { type} / { id}
批量操作bulk
Bulk请求体格式:
{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n
每行必须以 "\n" 符号结尾,包括最后一行。这是作为每行有效的分离而做的标记。
POST _bulk
{ "delete" : { "_index" : "itsource" , "_type" : "employee" , "_id" : "123" } }
{ "create" : { "_index" : "itsource" , "_type" : "blog" , "_id" : "123" } }
{ "title" : "我发布的博客" }
{ "index" : { "_index" : "itsource" , "_type" : "blog" } }
{ "title" : "我的第二博客" }
3.文档的简单查询
通过文档ID获取`GET crm/employee/1
批量获取
方式一
GET _mget (了解)
{
"docs" : [
{
"_index" : "itsource" ,
"_type" : "blog" ,
"_id" : 2
} ,
{
"_index" : "itsource" ,
"_type" : "employee" ,
"_id" : 1 ,
"_source" : "email,age"
}
]
}
方式二
GET itsource/ blog/ _mget
{
"ids" : [ "2" , "1" ]
}
空搜索:没有指定任何的查询条件,只返回集群索引中的所有文档`GET _search码片`
分页搜索:和SQL使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch接受 from 和 size 参数:
size : 每页条数,默认 10
from : 跳过开始的结果数,默认 0
GET _search? size= 5
GET _search? size= 5 & from= 5
GET _search? size= 5 & from= 10
查询字符串搜索
查询年龄为25 岁的员工
GET itsource/ employee/ _search? q= age: 25
4.DSL查询与过滤
1.什么是DSL查询
由ES提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询,DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现
GET itsource/ employee/ _search? q= fullName: 小张
GET itsource/ employee/ _search
{
"query" : {
"match" : {
"fullName" : "小张"
}
}
}
模糊匹配
GET itsource/ employee/ _search
{
"query" : {
"wildcard" : { "name" : "*zs*" }
}
}
2.DSL查询:使用DSL查询,必须要传递query参数给ES
{
"query" : {
"match_all" : { }
} ,
"from" : 20 ,
"size" : 10 ,
"_source" : [ "fullName" , "age" , "email" ] ,
"sort" : [ { "join_date" : "desc" } , { "age" : "asc" } ]
}
表示 查询所有数据,查询fullName,age和email,按照加入日期和年龄进行排序
3.DSL过滤:DSL过滤 查询文档的方式更像是对于我的条件“有”或者“没有”,--精确查询
{
"query" : {
"bool" : { 与( must) 或( should) 非( must not)
"must" : [
{ "match" : { "description" : "search" } }
] ,
"filter" : {
"term" : { "tags" : "lucene" }
}
}
} ,
"from" : 20 ,
"size" : 10 ,
"_source" : [ "fullName" , "age" , "email" ] ,
"sort" : [ { "join_date" : "desc" } , { "age" : "asc" } ]
}
4.DSL过滤和DSL查询在性能上的区别 :
过滤结果可以缓存并应用到后续请求;
查询语句同时 匹配文档,计算相关性,所以更耗时,且不缓存。
过滤语句 可有效地配合查询语句完成文档过滤。
原则上,使用DSL查询 做模糊查询 或其他需要进行相关性评分的场景,其它全用DSL过滤。
5.使用DSL查询与过滤
① 全匹配(match_all)
普通搜索(匹配所有文档):
{
"query" : {
"match_all" : { }
}
}
如果需要使用过滤条件(在所有文档中过滤,bool部分默认可不写):
GET _search
{
"query" : {
"bool" : {
"must" : [
{
"match_all" : { }
}
] ,
"filter" : {
"term" : {
"name" : "zs1"
}
}
}
}
}
② 标准查询(match和multi_match)
match查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。如果你使用match查询一个全文本字段,它会在真正查询之前用分析器先分析查询字符:
{
"query" : {
"match" : {
"fullName" : "Steven King"
}
}
}
multi_match 查询允许你做 match查询的基础上同时搜索多个字段:
{
"query" : {
"multi_match" : {
"query" : "Steven King" ,
"fields" : [ "fullName" , "title" ]
}
}
}
#fullName = ‘steven King’ or tile = ‘steven King’
③单词搜索与过滤(Term和Terms)
term搜索与过滤
{
"query" : {
"bool" : {
"must" : {
"match_all" : { }
} ,
"filter" : {
"term" : {
"tags" : "elasticsearch"
}
}
}
}
}
Terms搜索与过滤
{
"query" : {
"terms" : {
"tags" : [ "jvm" , "hadoop" , "lucene" ] ,
"minimum_match" : 2
}
}
}
#minimum_match: 至少匹配个数,默认为1
④ 组合条件搜索与过滤(Bool):组合搜索bool可以组合多个查询条件为一个查询对象,查询条件包括must、should和must_not。
{
"query" : {
"bool" : {
"must" : [ { "term" : { "hobby" : "美女" } } ] ,
"should" : [ { "term" : { "hobby" : "游戏" } } ,
{ "term" : { "hobby" : "运动" } }
] ,
"must_not" : [
{ "range" : { "birth_date" : { "lt" : "1990-06-30" } } }
] ,
"filter" : [ . . . ] ,
"minimum_should_match" : 1
}
}
}
#Hobby= 美女 and (hobby= 游戏 or hobby= 运动) and birth_date >= 1990 - 06 - 30
#提示: 如果 bool 查询下没有must子句,那至少应该有一个should子句。但是 如果有 must子句,那么没有 should子句也可以进行查询。
⑤ 范围查询与过滤(range)
{
"query" : {
"range" : {
"age" : {
"gte" : 20 ,
"lt" : 30
}
}
}
}
#上例中查询年龄大于等于20 并且小于30 。
#gt: > gte: >= lt: < lte: <=
⑥ 存在和缺失过滤器(exists和missing)
{
"query" : {
"bool" : {
"must" : [ {
"match_all" : { }
} ] ,
"filter" : {
"exists" : { "field" : "gps" }
}
}
}
}
#exists和missing只能用于过滤结果。
⑦ 前匹配搜索与过滤(prefix):和term查询相似,前匹配搜索不是精确匹配,而是类似于SQL中的like ‘key%’
{
"query" : {
"prefix" : {
"fullName" : "张"
}
}
}
#查询姓张的所有人
⑧ 通配符搜索(wildcard):使用*代表0~N个,使用?代表1个
{
"query" : {
"wildcard" : {
"fullName" : "倪*华"
}
}
}
四.IK分词器
ES的IK分词器插件源码地址:https://github.com/medcl/elasticsearch-analysis-ik
① Maven打包IK插件
② 解压target/releases/elasticsearch-analysis-ik-5.2.2.zip文件,并将其内容放置于ES根目录/plugins/ik
③ 重启ES
④ 测试分词器
POST _analyze
{
"analyzer" : "ik_smart" ,
"text" : "中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首"
}
分词器类型
ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。
ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;
文档映射Mapper:ES的文档映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型。
① 基本字段类型
字符串:text(分词),keyword(不分词) StringField(不分词文本),TextFiled(要分词文本);text默认为全文文本,keyword默认为非全文文本
数字:long,integer,short,double,float
日期:date
逻辑:boolean
② 复杂数据类型
对象类型:object
数组类型:array
地理位置:geo_point,geo_shape
默认映射
查看索引类型的映射配置:GET {indexName}/_mapping/{typeName}
ES在没有配置Mapping的情况下新增文档,ES会尝试对字段类型进行猜测,并动态生成字段和类型的映射关系。
JSON type Field type
Boolean: true or false "boolean"
Whole number: 123 "long"
Floating point: 123.45 "double"
String, valid date:"2014-09-15" "date"
String: "foo bar" "string"
简单类型映射
type 类型:基本数据类型,integer,long,date,boolean,keyword,text...
enable 是否启用:默认为true。 false:不能索引、不能搜索过滤,仅在_source中存储
boost 权重提升倍数:用于查询时加权计算最终的得分。
format 格式:一般用于指定日期格式,如 yyyy-MM-dd HH:mm:ss.SSS
ignore_above 长度限制:长度大于该值的字符串将不会被索引和存储。
ignore_malformed 转换错误忽略:true代表当格式转换错误时,忽略该值,被忽略后不会被存储和索引。
include_in_all 是否将该字段值组合到_all中。
null_value 默认控制替换值。如空字符串替换为”NULL”,空数字替换为-1
store 是否存储:默认为false。true意义不大,因为_source中已有数据
index 索引模式:analyzed (索引并分词,text默认模式), not_analyzed (索引不分词,keyword默认模式),no(不索引)
analyzer 索引分词器:索引创建时使用的分词器,如ik_smart,ik_max_word,standard
search_analyzer 搜索分词器:搜索该字段的值时,传入的查询内容的分词器。
fields 多字段索引:当对该字段需要使用多种索引模式时使用。
如:城市搜索New York 下面字段city既可以分词有可以不分词
"city": {
"type": "text",
"analyzer": "ik_smart",
"fields": {
"raw": {
"type": "keyword"
}
}
}
有些类型 有时候需要分词 有时候不需要分词
city分词
city.raw 不分词
那么以后搜索过滤和排序就可以使用city.raw字段名
针对单个类型的映射配置方式
查询映射类型:`GET shop/goods/_mapping`
修改映射类型
(1 )Delete shop;
(2 )PUT shop;
(3 )POST shop/ goods/ _mapping{
"goods" : {
"properties" : {
"price" : {
"type" : "integer"
} ,
"name" : {
"type" : "text" ,
"analyzer" : "ik_smart" ,
"search_analyzer" : "ik_smart"
}
}
}
}
( 4 ) 加入数据
put shop/ goods/ 1
{
"price" : 88 ,
"name" : "iphone8"
}
注意:你可以在第一次创建索引的时候指定映射的类型。此外,你也可以晚些时候为新类型添加映射(或者为已有的类型更新映射)。你可以向已有映射中增加字段,但你不能修改它。如果一个字段在映射中已经存在,这可能意味着那个字段的数据已经被索引。如果你改变了字段映射,那已经被索引的数据将错误并且不能被正确的搜索到。
同时对多个类型的映射配置方式
PUT { indexName}
{
"mappings" : {
"user" : {
"properties" : {
"id" : {
"type" : "integer"
} ,
"info" : {
"type" : "text" ,
"analyzer" : "ik_smart" ,
"search_analyzer"
}
}
} ,
"dept" : {
"properties" : {
"id" : {
"type" : "integer"
} ,
. . . . 更多字段映射配置
}
}
}
}
对象及数组类型映射
① 对象的映射与索引
{
“id” : 1 ,
“girl” : {
“name” : “王小花”,
“age” : 22
}
}
#对应的mapping配置:
{
"properties" : {
"id" : {
"type" : "long" } ,
"girl" : {
"properties" : {
"name" : { "type" : "keyword" } ,
"age" : { "type" : "integer" }
}
}
}
}
② 数组与对象数组(注意:数组中元素的类型必须一致。)
{
“id” : 1 ,
“hobby” : [ “王小花”, “林志玲”]
}
#对应的mapping配置是:
{
"properties" : {
"id" : { "type" : "long" } ,
"hobby" : { "type" : "keyword" }
}
}
全局映射:全局映射可以通过动态模板和默认设置两种方式实现;索引下所有的类型映射配置会继承_default_的配置
PUT { indexName}
{
"mappings" : {
"_default_" : {
"_all" : {
"enabled" : false 关闭默认映射配置
}
} ,
"user" : {
} ,
"dept" : {
"_all" : {
"enabled" : true
}
} ,
. . . .
}
}
#默认的enabled= false 表示关闭模式的配置,如果你想用,在自己的配置里面开启配置
动态模板:dynamic_templates
"name" : {
"type" : "text" ,
"fields" : {
"keyword" : {
"type" : "keyword" ,
"ignore_above" : 256
}
}
}
在实际应用场景中,一个对象的属性中,需要全文检索的字段较少,大部分字符串不需要分词,因此,需要利用全局模板覆盖自带的默认模板:
PUT _template/ global_template
{
"template" : "*" ,
"settings" : { "number_of_shards" : 1 } ,
"mappings" : {
"_default_" : {
"_all" : {
"enabled" : false
} ,
"dynamic_templates" : [
{
"string_as_text" : {
"match_mapping_type" : "string" ,
"match" : "*_text" ,
"mapping" : {
"type" : "text" ,
"analyzer" : "ik_max_word" ,
"search_analyzer" : "ik_max_word" ,
"fields" : {
"raw" : {
"type" : "keyword" ,
"ignore_above" : 256
}
}
}
}
} ,
{
"string_as_keyword" : {
"match_mapping_type" : "string" ,
"mapping" : {
"type" : "keyword"
}
}
}
]
}
} }
#上面的意思:就是如果索引库里面字段 以_text结尾 就需要进行分词,如果不是,就不分词
五.JavaAPI
1什么是JavaAPI
ES对Java提供一套操作索引库的工具包,即Java API。所有的ES操作都使用Client对象执行
导包
< dependency>
< groupId> org. elasticsearch. client< / groupId>
< artifactId> transport< / artifactId>
< version> 5.2 .2 < / version>
< / dependency>
测试
public class Es_Test {
public TransportClient getClient ( ) throws Exception{
Settings settings = Settings. builder ( )
. put ( "client.transport.sniff" , true )
. put ( "cluster.name" , "my-ealsticsearch" ) .
build ( ) ;
TransportClient client = new PreBuiltTransportClient ( settings)
. addTransportAddress ( new InetSocketTransportAddress ( InetAddress. getByName ( "127.0.0.1" ) , 9301 ) ) ;
return client;
}
@Test
public void save ( ) throws Exception{
TransportClient client = getClient ( ) ;
IndexRequestBuilder indexRequestBuilder = client. prepareIndex ( "ehr" , "emp" , "1" ) ;
Map< String, Object> map= new HashMap < String, Object> ( ) ;
map. put ( "name" , "kd" ) ;
map. put ( "id" , 1 ) ;
indexRequestBuilder. setSource ( map) ;
IndexResponse indexResponse = indexRequestBuilder. get ( ) ;
System. err. println ( indexResponse) ;
}
@Test
public void update ( ) throws Exception{
TransportClient client = getClient ( ) ;
UpdateRequestBuilder updateRequestBuilder = client. prepareUpdate ( "ehr" , "emp" , "1" ) ;
Map< String, Object> map= new HashMap < String, Object> ( ) ;
map. put ( "name" , "kd2" ) ;
map. put ( "id" , 1 ) ;
updateRequestBuilder. setDoc ( map) ;
UpdateResponse updateResponse = updateRequestBuilder. get ( ) ;
System. err. println ( updateResponse) ;
}
@Test
public void get ( ) throws Exception{
TransportClient client = getClient ( ) ;
GetRequestBuilder getRequestBuilder = client. prepareGet ( "ehr" , "emp" , "1" ) ;
GetResponse getFields = getRequestBuilder. get ( ) ;
System. err. println ( getFields. getSource ( ) ) ;
}
@Test
public void delete ( ) throws Exception{
TransportClient client = getClient ( ) ;
DeleteRequestBuilder deleteRequestBuilder = client. prepareDelete ( "ehr" , "emp" , "1" ) ;
DeleteResponse deleteResponse = deleteRequestBuilder. get ( ) ;
System. err. println ( deleteResponse) ;
}
@Test
public void bulk ( ) throws Exception{
TransportClient client = getClient ( ) ;
BulkRequestBuilder bulkRequest = client. prepareBulk ( ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
Map< String, Object> map= new HashMap < String, Object> ( ) ;
map. put ( "name" , "iPhone" ) ;
map. put ( "price" , 1000 * i) ;
map. put ( "local" , "cn" ) ;
map. put ( "id" , i) ;
IndexRequestBuilder indexRequestBuilder = client. prepareIndex ( "ehr" , "emp" , i+ "" ) ;
indexRequestBuilder. setSource ( map) ;
bulkRequest. add ( indexRequestBuilder) ;
}
BulkResponse bulkResponse = bulkRequest. get ( ) ;
if ( bulkResponse. hasFailures ( ) ) {
System. err. println ( "批量失败" ) ;
}
}
@Test
public void search ( ) throws Exception{
TransportClient client = getClient ( ) ;
BoolQueryBuilder bool= QueryBuilders. boolQuery ( ) ;
bool. must ( new MatchQueryBuilder ( "name" , "iphone" ) ) ;
List< QueryBuilder> filter = bool. filter ( ) ;
filter. add ( new TermQueryBuilder ( "local" , "cn" ) ) ;
filter. add ( new RangeQueryBuilder ( "price" ) . gte ( 7000 ) . lte ( 10000 ) ) ;
SearchResponse searchResponse =
client. prepareSearch ( "ehr" ) . setTypes ( "emp" ) . setFrom ( 0 ) . setSize ( 2 )
. addSort ( "price" , SortOrder. DESC)
. setQuery ( bool)
. get ( ) ;
SearchHit[ ] hits = searchResponse. getHits ( ) . getHits ( ) ;
for ( SearchHit hit : hits) {
System. err. println ( hit. getSource ( ) ) ;
}
}
}
springboot集成elasticsearch
导包
< dependencies>
< ! -- springboot 对spring data es支持-- >
< dependency>
< groupId> org. springframework. boot< / groupId>
< artifactId> spring- boot- starter- data- elasticsearch< / artifactId>
< / dependency>
< / dependencies>
yml配置
spring:
data:
elasticsearch:
cluster- name: elasticsearch
cluster- nodes: 127.0 .0 .1 : 9300
测试
@Data
@Document ( indexName = "shop" , type = "GoodsEo" )
public class GoodsEo {
@Id
private Long id;
@Field ( type = FieldType. Text, analyzer = "ik_max_word" )
private String name;
@Field ( type = FieldType. Keyword)
private String country;
@Field ( type = FieldType. Double)
private double price;
}
@RunWith ( SpringJUnit4ClassRunner. class )
@SpringBootTest ( classes = ESRunApp. class )
public class Es_Test {
@Autowired
ElasticsearchTemplate elasticsearchTemplate;
@Autowired
GoodsRepo goodsRepo;
@Test
public void createIndex ( ) throws Exception{
elasticsearchTemplate. createIndex ( GoodsEo. class ) ;
elasticsearchTemplate. putMapping ( GoodsEo. class ) ;
}
@Test
public void save ( ) throws Exception{
GoodsEo goodsEo= new GoodsEo ( ) ;
goodsEo. setId ( 1 L) ;
goodsEo. setName ( "iphone" ) ;
goodsEo. setCountry ( "cn" ) ;
goodsEo. setPrice ( 1100 ) ;
goodsRepo. save ( goodsEo) ;
}
@Test
public void saveALL ( ) throws Exception{
List< GoodsEo> list= new ArrayList < > ( ) ;
for ( long i = 0 ; i < 10 ; i++ ) {
GoodsEo goodsEo= new GoodsEo ( ) ;
goodsEo. setId ( i) ;
goodsEo. setName ( "iphone" ) ;
goodsEo. setCountry ( "cn" ) ;
goodsEo. setPrice ( 1000 * i) ;
list. add ( goodsEo) ;
}
goodsRepo. saveAll ( list) ;
}
@Test
public void del ( ) throws Exception{
goodsRepo. deleteById ( 1 L) ;
}
@Test
public void saveupdte ( ) throws Exception{
GoodsEo goodsEo= new GoodsEo ( ) ;
goodsEo. setId ( 2 L) ;
goodsEo. setName ( "iphone" ) ;
goodsEo. setCountry ( "cn" ) ;
goodsEo. setPrice ( 2100 ) ;
goodsRepo. save ( goodsEo) ;
}
@Test
public void findAll ( ) throws Exception{
Iterable< GoodsEo> all = goodsRepo. findAll ( ) ;
for ( GoodsEo goodsEo : all) {
System. err. println ( goodsEo) ;
}
}
@Test
public void query ( ) throws Exception{
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder ( ) ;
BoolQueryBuilder boolQueryBuilder= new BoolQueryBuilder ( ) ;
boolQueryBuilder. must ( new MatchQueryBuilder ( "name" , "iphone" ) ) ;
List< QueryBuilder> filter = boolQueryBuilder. filter ( ) ;
filter. add ( new TermQueryBuilder ( "country" , "cn" ) ) ;
nativeSearchQueryBuilder. withSort ( new FieldSortBuilder ( "price" ) . order ( SortOrder. DESC) ) ;
nativeSearchQueryBuilder. withPageable ( PageRequest. of ( 0 , 3 ) ) ;
Page< GoodsEo> page = goodsRepo. search ( nativeSearchQueryBuilder. build ( ) ) ;
System. err. println ( "总条数:" + page. getTotalElements ( ) ) ;
for ( GoodsEo goodsEo : page) {
System. err. println ( goodsEo) ;
}
}
}