注意:
1.基于Version 6.*
基础概念
index 索引,相当于mysql的database
(1)代表一个document存放在哪个index中
(2)类似的数据放在一个索引,非类似的数据放不同索引:product index(包含了所有的商品),sales index(包含了所有的商品销售数据),inventory index(包含了所有库存相关的数据)。如果你把比如product,sales,human resource(employee),全都放在一个大的index里面,比如说company index,不合适的。
(3)index中包含了很多类似的document:类似是什么意思,其实指的就是说,这些document的fields很大一部分是相同的,你说你放了3个document,每个document的fields都完全不一样,这就不是类似了,就不太适合放到一个index里面去了。
(4)索引名称必须是小写的,不能用下划线开头,不能包含逗号。
type 表名 相当于mysql的table
(1)代表document属于index中的哪个类别(type)
(2)一个索引通常会划分为多个type,逻辑上对index中有些许不同的几类数据进行分类:因为一批相同的数据,可能有很多相同的fields,但是还是可能会有一些轻微的不同,可能会有少数fields是不一样的,举个例子,就比如说,商品,可能划分为电子商品,生鲜商品,日化商品,等等。
(3)type名称可以是大写或者小写,但是同时不能用下划线开头,不能包含逗号
mapping 表结构 相当于mysql的字段列表
field 表字段
_id 表主键 创建Mapping的时候不需要显示定义
(1)代表document的唯一标识,与index和type一起,可以唯一标识和定位一个document
(2)我们可以手动指定document的id(put /index/type/id),也可以不指定,由es自动为我们创建一个id
一、插入
PUT /ecommerce/product/1
{
"name" : "gaolujie yagao",
"desc" : "gaoxiao meibai",
"price" : 30,
"producer" : "gaolujie producer",
"tags": [ "meibai", "fangzhu" ]
}
PUT /ecommerce/product/2
{
"name" : "jiajieshi yagao",
"desc" : "youxiao fangzhu",
"price" : 25,
"producer" : "jiajieshi producer",
"tags": [ "fangzhu" ]
}
PUT /ecommerce/product/3
{
"name" : "zhonghua yagao",
"desc" : "caoben zhiwu",
"price" : 40,
"producer" : "zhonghua producer",
"tags": [ "qingxin" ]
}
插入数据时的id生成策略:
1.手动指定document id:当es的数据来源于其他系统,比如mysql而这些数据都有主键ID,建议使用该方式,可以直接将mysql的主键作为es中的ID。
2.自动生成的id:当我们的数据是直接存储到es中时,建议采用该方式。该方式的特点,长度为20个字符,URL安全,base64编码,GUID,分布式系统并行生成时不可能会发生冲突。
POST /test_index/test_type
{
"test_content": "my test"
}
结果:
{
"_index": "test_index",
"_type": "test_type",
"_id": "fkSoJ2EBuYE9HLKhxglD",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
二、修改
1、document的全量替换型修改:该方式是直接替换类型是 product id=1的文档。该方式必须带上所有的field,才能去进行信息的修改。
PUT /ecommerce/product/1
{
"name" : "jiaqiangban gaolujie yagao",
"desc" : "gaoxiao meibai",
"price" : 30,
"producer" : "gaolujie producer",
"tags": [ "meibai", "fangzhu" ]
}
注意:
1、document的全量替换
(1)语法与创建文档是一样的,如果document id不存在,那么就是创建;如果document id已经存在,那么就是全量替换操作,替换document的json串内容
(2)document是不可变的,如果要修改document的内容,第一种方式就是全量替换,直接对document重新建立索引,替换里面所有的内容
(3)es会将老的document标记为deleted,然后新增我们给定的一个document,当我们创建越来越多的document的时候,es会在适当的时机在后台启动线程删除标记为deleted的document,释放空间。
2、partial update 指定修改内容修改(推荐该方式)
POST /ecommerce/product/1/_update
{
"doc": {
"name": "jiaqiangban gaolujie yagao"
}
}
该方式发生的步骤与全量替换基本一样,不过该方式将这些流程放在es内部,这样减少了放了请求,减少并发冲突发的概率。他同样会生成两份document,老的document标记为deleted,partial update 修改的数据更新到新的document中。
retry_on_conflict(重策略):当执行索引和更新的时候,有可能另一个进程正在执行更新。这个时候就会造成冲突,这个参数就是用于定义当遇到冲突时,再过多长时间执行操作,通过retry_on_conflict参数设置重试次数来自动完成,这样update操作将会在发生错误前重试——这个值默认为0。
例如:
POST /test_index/test_type/1/_update?retry_on_conflict=5
{
"doc": {
"test_name": "update book re",
"test_id":11122
}
}
三、删除
根据类型的Id直接删除
DELETE /ecommerce/product/1
注意: 不是物理删除,只会将其标记为deleted,当数据越来越多的时候,在后台启动线程自动删除,释放空间。
四、查询
(一)、query string search
什么是query string search:search参数都是以http请求的query string来附带的
语法:
/_search
在所有的索引中搜索所有的类型
/gb/_search
在 gb 索引中搜索所有的类型
/gb,us/_search
在 gb 和 us 索引中搜索所有的文档
/g*,u*/_search
在任何以 g 或者 u 开头的索引中搜索所有的类型
/gb/user/_search
在 gb 索引中搜索 user 类型
/gb,us/user,tweet/_search
在 gb 和 us 索引中搜索 user 和 tweet 类型
/_all/user,tweet/_search
在所有的索引中搜索 user 和 tweet 类型
当在单一的索引下进行搜索的时候,Elasticsearch 转发请求到索引的每个分片中,可以是主分片也可以是副本分片,然后从每个分片中收集结果。多索引搜索恰好也是用相同的方式工作的--只是会涉及到更多的分片。
demo:
1、搜索全部商品
GET /ecommerce/product/_search
{
}
结果分析:
took:耗费了几毫秒
timed_out:是否超时,这里是没有
_shards:数据拆成了5个分片,所以对于搜索请求,会打到所有的primary shard(或者是它的某个replica shard也可以)
hits.total:查询结果的数量,3个document
hits.max_score:score的含义,就是document对于一个search的相关度的匹配分数,越相关,就越匹配,分数也高
hits.hits:包含了匹配搜索的document的详细数据
2、搜索商品名称中包含yagao的商品,并按照价格倒序:
GET /ecommerce/product/_search?q=name:yagao&sort=price:desc
{
}
(二)、query DSL
DSL:Domain Specified Language,特定领域的语言
http request body:请求体,可以用json的格式来构建查询语法,比较方便,可以构建各种复杂的语法。
实际开发中,基本都是组合多条件查询。elasticsearch提供bool来实现这种需求;
主要参数:
must:文档 必须 匹配这些条件才能被包含进来。
must_not:文档 必须不 匹配这些条件才能被包含进来。
should:如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
filter:必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
1、分页查询所有商品并按照价格倒序
GET /ecommerce/product/_search
{
"query": { "match_all": {} },
"sort": [
{ "price": "desc" }
],
"from": 0,
"size": 3
}
query:包装查询条件
sort:包装排序条件
form:数据偏移量
size:展示条数 类似于mysql中 limit offst,length 中那个的length
_source: 给出要查询的字段列表
2、根据名称查询,并按照价格倒序
GET /ecommerce/product/_search
{
"query": { "match": {
"name":"yagao"
}
},
"sort": [
{ "price": "desc" }
]
}
3、指定返回结果的列(field)
GET /ecommerce/product/_search
{
"query": { "match_all": {} },
"_source": ["name", "price"]
}
_source元数据:就是说,我们在创建一个document的时候,使用的那个放在request body中的json串,默认情况下,在get的时候,会原封不动的给我们返回回来。我们可以指定_source中,返回哪些field。
get test_aoshi2/test_type/_search
{
"query":{
"bool":{
"must":{
"terms":{
"s_arr":["p","j","y","c"]
}
}
}
}
}
(三)、query filter
1、搜索商品名称包含yagao,而且售价大于25元的商品
GET /ecommerce/product/_search
{
"query" : {
"bool" : {
"must" : {
"match" : {
"name" : "yagao"
}
},
"filter" : {
"range" : {
"price" : { "gt" : 25 }
}
}
}
}
}
(四)full-text search (全文检索)
全文检索会将输入的搜索串拆解开来,去倒排索引里面去一一匹配,只要能匹配上任意一个拆解后的单词,就可以作为结果返回。
1、根据一段文字进行查找
GET /ecommerce/product/_search
{
"query" : {
"match" : {
"producer" : "yagao producer"
}
}
}
producer这个字段,会先被拆解,建立倒排索引。根据es中这个字段的所有数据分词后有 jiajieshi、gaolujie、zhonghua、producer
查询条件producer 也会被分词 ,分词为 yagao 和 producer
结果分析: "max_score": 0.2876821 匹配度
(五)、phrase search (短语查找)
输入的搜索串分此后,进行完全顺序匹配。必须在指定的字段文本中,完全包含搜索串分此后的全部结果,才可以算匹配,作为结果返回。
slop参数其实就是你允许你输入的词里面中间还能隔着几个词。
例如:也就是说,slop=0就是只能按你输入的词来搜索(分词后顺序完全匹配)。slop=1,比如输入是“ni ma", 那么结果里可能就包含"ni ba ma"。
GET /ecommerce/product/_search
{
"query" : {
"match_phrase" : {
"producer" : {
"query":"yagao producer",
"slop":5
}
}
}
}
(六)、highlight search (高亮搜索结果)
GET /ecommerce/product/_search
{
"query":{
"bool":{
"should":[
{
"term":{
"media_id":0
}
},
{
"match_phrase":{
"name":"宝宝"
}
},
{
"match_phrase":{
"introduction":"宝宝"
}
}
]
}
},
"sort":[
{
"media_id":"desc"
}
],
"from":0,
"size":20,
"_source":[
"_id","name","introduction"
],
"highlight": {
"pre_tags" : ["<tag1 class=\"color_red\">"],
"post_tags" : ["</tag1>"],
"fields" : {
"introduction" : {},
"name" : {}
}
}
}
//结果
{
"took":308,
"timed_out":false,
"_shards":{
"total":9,
"successful":9,
"skipped":0,
"failed":0
},
"hits":{
"total":9426,
"max_score":null,
"hits":[
{
"_index":"video_mdedia_v1",
"_type":"video_mdedia",
"_id":"3781382",
"_score":null,
"_source":{
"name":"仓鼠宝宝",
"introduction":"仓鼠宝宝的世界"
},
"highlight":{
"name":[
"仓鼠<em>宝宝</em>"
],
"introduction":[
"仓鼠<em>宝宝</em>的世界"
]
},
"sort":[
3781382
]
}
]
}
}
(七)、数量统计 (_count)
get /test_aoshi2/tag/_count
{
"query":{
"bool":{
"should":[
{"match":{"_id":1}},
{"match":{"_id":20605}}
]
}
}
}
返回:
{
"count" : 2, //统计到的数量
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
}
}
(八、)
bool联合查询: must,should,must_not
must: 文档必须完全匹配条件
should: should下面会带一个以上的条件,至少满足一个条件,这个文档就符合should
must_not: 文档必须不匹配条件
级别分层:
bool : 下面只能有 must must_not should filter
must: 文档必须完全匹配条件
should: should下面会带一个以上的条件,至少满足一个条件,这个文档就符合should
must_not: 文档必须不匹配条件
fileter:过滤条件
匹配方式:
match
match_phrase:精确匹配 完全匹配可能比较严,我们会希望有个可调节因子,少匹配一个也满足,那就需要使用到slop。
multi_match : 如果我们希望两个字段进行匹配,其中一个字段有这个文档就满足的话,使用multi_match
best_fields:我们希望完全匹配的文档占的评分比较高
most_fields:我们希望越多字段匹配的文档评分越高
cross_fields:会希望这个词条的分词词汇是分配到不同字段中的
term:是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇
terms:是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇 ,搜索多个值 相当于in
range:区间匹配
exists: 类似于SQL中的not is null
missing: 类似于SQL中的 is null
下面是查询案例
// $url = $host . '/' . 'test_aoshi2/tag/_search';
// $data = array(
// 'query'=>array(
// 'bool'=>array( //多字段检索 (and结构) 结构开始
// 'must'=>array( //must相当于mysql中的 and
// array('match'=>array('_name'=>'生活')),
// array('match'=>array('_create_time'=>1596184876))
// )
// ) //多字段检索 (and结构) 结构结束
// 'bool'=>array( //多字段检索 (and结构 + 后过滤条件) 结构开始
// 'must'=>array( //must相当于mysql中的 and
// array('match'=>array('_name'=>'生活')),
// array('match'=>array('_create_time'=>1596184876))
// ),
// 'filter'=>array( //相当于mysql的having
// 'range'=>array( //区间函数
// '_create_time'=>array('gt'=>1)
// )
// )
// ), //多字段检索 (and结构 + 后过滤条件) 结构结束
// 'bool'=>array( //多字段检索 (and结构 + 后过滤条件) 结构开始
// 'must'=>array( //must相当于mysql中的 and
// array('match'=>array('_name'=>'生活')),
// array('match'=>array('_create_time'=>1596184876))
// ),
// 'should'=>array( //相当于mysql的having
// array('match'=>array('_name'=>'生活')),
// array('match'=>array('_name'=>'国际')),
// )
// ), //多字段检索 (and结构 + 后过滤条件) 结构结束
// 'bool'=>array( //多字段检索 (and结构并集取反) 结构开始
// 'must_not'=>array( //must相当于mysql中的 and取反
// array('match'=>array('_name'=>'生活')),
// array('match'=>array('_create_time'=>1596184823))
// )
// ) //多字段检索 (and结构并集取反) 结构结束
// 'bool'=>array( //多字段检索 (多字段or结构) 结构开始
// 'should'=>array( //must相当于mysql中的 or
// array('match'=>array('_name'=>'生活')),
// array('match'=>array('_create_time'=>1596184823))
// )
// ) //多字段检索 (多字段or结构) 结构结束
// 'bool'=>array( //多字段检索 (单字段or结构) 结构开始
// 'should'=>array( //must相当于mysql中的 or
// array('match'=>array('_name'=>'生活')),
// array('match'=>array('_name'=>'国际'))
// )
// ) //多字段检索 (单字段or结构) 结构结束
// 'match'=>array('_name'=>'生活'), //单字段检索
// ),
// 'sort'=>array('_score'=>'desc'), //排序 可以根据返回数据集的任意存在的字段排序 需要保证所有集合都含有这个字段
// '_source'=>array('_name','_tag'), //查询字段数
// 'from'=>2, //起始位置偏移量
// 'size'=>2 //每页显示数据量
// );
网友评论