Elasticsearch深入搜索之结构化搜索及JavaAPI的使用
一、Es中创建索引
1.创建索引:
在之前的Es插件的安装和使用中说到创建索引自定义分词器和创建type,当时是分开写的,其实创建索引时也可以创建type,并指定分词器。
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"ik_smart_pinyin": {
"type": "custom",
"tokenizer": "ik_smart",
"filter": ["my_pinyin", "word_delimiter"]
},
"ik_max_word_pinyin": {
"type": "custom",
"tokenizer": "ik_max_word",
"filter": ["my_pinyin", "word_delimiter"]
}
},
"filter": {
"my_pinyin": {
"type" : "pinyin",
"keep_separate_first_letter" : true,
"keep_full_pinyin" : true,
"keep_original" : true,
"limit_first_letter_length" : 16,
"lowercase" : true,
"remove_duplicated_term" : true
}
}
}
},
"mappings": {
"my_type":{
"properties": {
"id":{
"type": "integer"
},
"name":{
"type": "text",
"analyzer": "ik_max_word_pinyin"
},
"age":{
"type":"integer"
}
}
}
}
}
2.添加数据
POST /my_index/my_type/_bulk
{ "index": { "_id":1}}
{ "id":1,"name": "张三","age":20}
{ "index": { "_id": 2}}
{ "id":2,"name": "张四","age":22}
{ "index": { "_id": 3}}
{ "id":3,"name": "张三李四王五","age":20}
3.查看数据类型
GET /my_index/my_type/_mapping 结果:
{
"my_index": {
"mappings": {
"my_type": {
"properties": {
"age": {
"type": "integer"
},
"id": {
"type": "integer"
},
"name": {
"type": "text",
"analyzer": "ik_max_word_pinyin"
}
}
}
}
}
}
二、结合JAVA(在这之前需在项目中配置好es,网上有好多例子可以参考)
1.创建Es实体类
package com.example.es_query_list.entity.es; import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document; @Setter
@Getter
@Document(indexName = "my_index",type = "my_type")
public class User {
@Id
private Integer id;
private String name;
private Integer age;
}
2.创建dao层
package com.example.es_query_list.repository.es; import com.example.es_query_list.entity.es.User;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface EsUserRepository extends ElasticsearchRepository<User,Integer> {
}
三、基本工作完成后,开始查询
1.精确值查询
查询非文本类型数据
GET /my_index/my_type/_search
{
"query": {
"term": {
"age": {
"value": "20"
}
}
}
} 结果:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 1,
"_source": {
"name": "张三",
"age": 20
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "3",
"_score": 1,
"_source": {
"name": "李四",
"age": 20
}
}
]
}
}
2.查询文本类型
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 0,
"max_score": null,
"hits": []
}
}
这时小伙伴们可能看到查询结果为空,为什么精确匹配却查不到我输入的准确值呢???之前说过咱们在创建type时,字段指定的分词器,如果输入未被分析出来的词是查不到结果的,让我们证明一下!!!!
首先先查看一下咱们查询的词被分析成哪几部分
GET my_index/_analyze
{
"text":"张三李四王五",
"analyzer": "ik_max_word"
} 结果:
{
"tokens": [
{
"token": "张三李四",
"start_offset": 0,
"end_offset": 4,
"type": "CN_WORD",
"position": 0
},
{
"token": "张三",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 1
},
{
"token": "三",
"start_offset": 1,
"end_offset": 2,
"type": "TYPE_CNUM",
"position": 2
},
{
"token": "李四",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 3
},
{
"token": "四",
"start_offset": 3,
"end_offset": 4,
"type": "TYPE_CNUM",
"position": 4
},
{
"token": "王",
"start_offset": 4,
"end_offset": 5,
"type": "CN_CHAR",
"position": 5
},
{
"token": "五",
"start_offset": 5,
"end_offset": 6,
"type": "TYPE_CNUM",
"position": 6
}
]
}
结果说明,张三李四王五被没有被分析成张三李四王五,所以查询结果为空。
解决方法:更新type中字段属性值,自定义一个映射指定类型为keyword类型,该类型在es中是指不会被分词器分析,也就是说这就是传说中的准确不能再准确的值了
POST /my_index/_mapping/my_type
{
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word_pinyin",
"fields": {
"keyword":{ //自定义映射名
"type": "keyword"
}
}
}
}
}
设置好完成后,需将原有的数据删除在添加一遍,再次查询就能查到了
public List<User> termQuery() {
QueryBuilder queryBuilder = QueryBuilders.termQuery("age",20);
// QueryBuilder queryBuilder = QueryBuilders.termQuery("name.keyword","张三李四王五");
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withIndices("my_index")
.withTypes("my_type")
.withQuery(queryBuilder)
.build(); List<User> list = template.queryForList(searchQuery,User.class);
return list;
}
四、组合过滤器
布尔过滤器
注意:官方文档有点问题,在5.X后,filtered 被bool代替了,The filtered
query is replaced by the bool query。
一个 bool
过滤器由三部分组成:
{
"bool" : {
"must" : [],
"should" : [],
"must_not" : [],
}
}
must
所有的语句都 必须(must) 匹配,与 AND
等价。
must_not
所有的语句都 不能(must not) 匹配,与 NOT
等价。
should
至少有一个语句要匹配,与 OR
等价。
bool
过滤器的不同部分即可。GET /my_index/my_type/_search
{
"query" : {
"bool" : {
"should" : [
{ "term" : {"age" : 20}},
{ "term" : {"age" : 30}}
],
"must" : {
"term" : {"name.keyword" : "张三"}
}
}
}
}
public List<User> boolQuery() {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.should(QueryBuilders.termQuery("age",20));
boolQueryBuilder.should(QueryBuilders.termQuery("age",30));
boolQueryBuilder.must(QueryBuilders.termQuery("name.keyword","张三"));
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withIndices("my_index")
.withTypes("my_type")
.withQuery(boolQueryBuilder)
.build();
List<User> list = template.queryForList(searchQuery,User.class);
return list;
}
嵌套布尔过滤器
尽管 bool
是一个复合的过滤器,可以接受多个子过滤器,需要注意的是 bool
过滤器本身仍然还只是一个过滤器。 这意味着我们可以将一个 bool
过滤器置于其他 bool
过滤器内部,这为我们提供了对任意复杂布尔逻辑进行处理的能力。
GET /my_index/my_type/_search
{
"query" : {
"bool" : {
"should" : [
{ "term" : {"age" : 20}},
{ "bool" : {
"must": [
{"term": {
"name.keyword": {
"value": "李四"
}
}}
]
}}
]
}
}
} 结果:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 1,
"_source": {
"id": 1,
"name": "张三",
"age": 20
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "3",
"_score": 1,
"_source": {
"id": 3,
"name": "张三李四王五",
"age": 20
}
}
]
}
}
因为 term
和 bool
过滤器是兄弟关系,他们都处于外层的布尔逻辑 should
的内部,返回的命中文档至少须匹配其中一个过滤器的条件。
这两个 term
语句作为兄弟关系,同时处于 must
语句之中,所以返回的命中文档要必须都能同时匹配这两个条件。
五、查找多个精确值
GET my_index/my_type/_search
{
"query": {
"terms": {
"age": [
20,
22
]
}
}
} 结果:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "2",
"_score": 1,
"_source": {
"id": 2,
"name": "张四",
"age": 22
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 1,
"_source": {
"id": 1,
"name": "张三",
"age": 20
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "3",
"_score": 1,
"_source": {
"id": 3,
"name": "张三李四王五",
"age": 20
}
}
]
}
}
一定要了解 term
和 terms
是 包含(contains) 操作,而非 等值(equals) (判断)。
TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("age",list);
六、范围查询
1、数字范围查询
GET my_index/my_type/_search
{
"query": {
"range": {
"age": {
"gte": 10,
"lte": 20
}
}
}
} 结果:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 1,
"_source": {
"id": 1,
"name": "张三",
"age": 20
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "3",
"_score": 1,
"_source": {
"id": 3,
"name": "张三李四王五",
"age": 20
}
}
]
}
}
注:gt(大于) gte(大于等于) lt(小于) lte(小于等于)
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").gte(10).lte(20);
2.对于时间范围查询
更新type,添加时间字段
POST /my_index/_mapping/my_type
{
"properties": {
"date":{
"type":"date",
"format":"yyyy-MM-dd"
}
}
}
添加数据:
POST /my_index/my_type/_bulk
{ "index": { "_id":4}}
{ "id":4,"name": "赵六","age":20,"date":"2018-10-1"}
{ "index": { "_id": 5}}
{ "id":5,"name": "对七","age":22,"date":"2018-11-20"}
{ "index": { "_id": 6}}
{ "id":6,"name": "王八","age":20,"date":"2018-7-28"}
查询:
GET my_index/my_type/_search
{
"query": {
"range": {
"date": {
"gte": "2018-10-20",
"lte": "2018-11-29"
}
}
}
} 结果:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "5",
"_score": 1,
"_source": {
"id": 5,
"name": "对七",
"age": 22,
"date": "2018-11-20"
}
}
]
}
}
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("date").gte("2018-10-20").lte("2018-11-29");
七、处理null值
1.添加数据
POST /my_index/posts/_bulk
{ "index": { "_id": "1" }}
{ "tags" : ["search"] }
{ "index": { "_id": "2" }}
{ "tags" : ["search", "open_source"] }
{ "index": { "_id": "3" }}
{ "other_field" : "some data" }
{ "index": { "_id": "4" }}
{ "tags" : null }
{ "index": { "_id": "5" }}
{ "tags" : ["search", null] }
2.查询指定字段存在的数据
GET /my_index/posts/_search
{
"query" : {
"constant_score" : { //不在去计算评分,默认都是1
"filter" : {
"exists" : { "field" : "tags" }
}
}
}
} 结果:
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "my_index",
"_type": "posts",
"_id": "5",
"_score": 1,
"_source": {
"tags": [
"search",
null
]
}
},
{
"_index": "my_index",
"_type": "posts",
"_id": "2",
"_score": 1,
"_source": {
"tags": [
"search",
"open_source"
]
}
},
{
"_index": "my_index",
"_type": "posts",
"_id": "1",
"_score": 1,
"_source": {
"tags": [
"search"
]
}
}
]
}
}
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.filter(QueryBuilders.constantScoreQuery(QueryBuilders.existsQuery("tags")));
3.查询指定字段缺失数据
注:Filter Query Missing 已经从 ES 5 版本移除
GET /my_index/posts/_search
{
"query" : {
"bool": {
"must_not": [
{"constant_score": {
"filter": {
"exists": {
"field": "tags"
}}
}}
]
}
}
} 查询结果:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "my_index",
"_type": "posts",
"_id": "4",
"_score": 1,
"_source": {
"tags": null
}
},
{
"_index": "my_index",
"_type": "posts",
"_id": "3",
"_score": 1,
"_source": {
"other_field": "some data"
}
}
]
}
}
注:处理null值,当字段内容为空时,将自定义将其当做为null值处理
boolQueryBuilder.mustNot(QueryBuilders.boolQuery().filter(QueryBuilders.constantScoreQuery(QueryBuilders.existsQuery("tags"))));
八、关于缓存
1.核心
其核心实际是采用一个 bitset 记录与过滤器匹配的文档。Elasticsearch 积极地把这些 bitset 缓存起来以备随后使用。一旦缓存成功,bitset 可以复用 任何 已使用过的相同过滤器,而无需再次计算整个过滤器。
这些 bitsets 缓存是“智能”的:它们以增量方式更新。当我们索引新文档时,只需将那些新文档加入已有 bitset,而不是对整个缓存一遍又一遍的重复计算。和系统其他部分一样,过滤器是实时的,我们无需担心缓存过期问题。
2.独立的过滤器缓存
属于一个查询组件的 bitsets 是独立于它所属搜索请求其他部分的。这就意味着,一旦被缓存,一个查询可以被用作多个搜索请求。bitsets 并不依赖于它所存在的查询上下文。这样使得缓存可以加速查询中经常使用的部分,从而降低较少、易变的部分所带来的消耗。
同样,如果单个请求重用相同的非评分查询,它缓存的 bitset 可以被单个搜索里的所有实例所重用。
让我们看看下面例子中的查询,它查找满足以下任意一个条件的电子邮件:
查询条件(例子):(1)在收件箱中,且没有被读过的 (2)不在 收件箱中,但被标注重要的
GET /inbox/emails/_search
{
"query": {
"constant_score": {
"filter": {
"bool": {
"should": [
{ "bool": { 1
"must": [
{ "term": { "folder": "inbox" }},
{ "term": { "read": false }}
]
}},
{ "bool": { 2
"must_not": {
"term": { "folder": "inbox" }
},
"must": {
"term": { "important": true }
}
}}
]
}
}
}
}
}
1和2共用的一个过滤器,所以使用同一个bitset
尽管其中一个收件箱的条件是 must
语句,另一个是 must_not
语句,但他们两者是完全相同的。这意味着在第一个语句执行后, bitset 就会被计算然后缓存起来供另一个使用。当再次执行这个查询时,收件箱的这个过滤器已经被缓存了,所以两个语句都会使用已缓存的 bitset 。
这点与查询表达式(query DSL)的可组合性结合得很好。它易被移动到表达式的任何地方,或者在同一查询中的多个位置复用。这不仅能方便开发者,而且对提升性能有直接的益处。
3.自动缓存行为
在 Elasticsearch 的较早版本中,默认的行为是缓存一切可以缓存的对象。这也通常意味着系统缓存 bitsets 太富侵略性,从而因为清理缓存带来性能压力。不仅如此,尽管很多过滤器都很容易被评价,但本质上是慢于缓存的(以及从缓存中复用)。缓存这些过滤器的意义不大,因为可以简单地再次执行过滤器。
检查一个倒排是非常快的,然后绝大多数查询组件却很少使用它。例如 term
过滤字段 "user_id"
:如果有上百万的用户,每个具体的用户 ID 出现的概率都很小。那么为这个过滤器缓存 bitsets 就不是很合算,因为缓存的结果很可能在重用之前就被剔除了。
这种缓存的扰动对性能有着严重的影响。更严重的是,它让开发者难以区分有良好表现的缓存以及无用缓存。
为了解决问题,Elasticsearch 会基于使用频次自动缓存查询。如果一个非评分查询在最近的 256 次查询中被使用过(次数取决于查询类型),那么这个查询就会作为缓存的候选。但是,并不是所有的片段都能保证缓存 bitset 。只有那些文档数量超过 10,000 (或超过总文档数量的 3% )才会缓存 bitset 。因为小的片段可以很快的进行搜索和合并,这里缓存的意义不大。
一旦缓存了,非评分计算的 bitset 会一直驻留在缓存中直到它被剔除。剔除规则是基于 LRU 的:一旦缓存满了,最近最少使用的过滤器会被剔除。
Elasticsearch深入搜索之结构化搜索及JavaAPI的使用的更多相关文章
- Elasticsearch结构化搜索与查询
Elasticsearch 的功能之一就是搜索,搜索主要分为两种类型,结构化搜索和全文搜索.结构化搜索是指有关查询那些具有内在结构数据的过程.比如日期.时间和数字都是结构化的:它们有精确的格式,我们可 ...
- ElasticSearch 2 (13) - 深入搜索系列之结构化搜索
ElasticSearch 2 (13) - 深入搜索系列之结构化搜索 摘要 结构化查询指的是查询那些具有内在结构的数据,比如日期.时间.数字都是结构化的.它们都有精确的格式,我们可以对这些数据进行逻 ...
- ElasticSearch 结构化搜索
1.介绍 结构化搜索(Structured search) 是指有关探询那些具有内在结构数据的过程.比如日期.时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作. 比较常见的操作 ...
- ElasticSearch常用结构化搜索
最近,需要用到ES的一些常用的结构化搜索命令,因此,看了一些官方的文档,学习了一下.结构化查询指的是查询那些具有内在结构的数据,比如日期.时间.数字都是结构化的. 它们都有精确的格式,我们可以对这些数 ...
- ElasticStack学习(九):深入ElasticSearch搜索之词项、全文本、结构化搜索及相关性算分
一.基于词项与全文的搜索 1.词项 Term(词项)是表达语意的最小单位,搜索和利用统计语言模型进行自然语言处理都需要处理Term. Term的使用说明: 1)Term Level Query:Ter ...
- elasticsearch 深入 —— 结构化搜索
结构化搜索 结构化搜索(Structured search) 是指有关探询那些具有内在结构数据的过程.比如日期.时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作.比较常见的操作 ...
- Elasticsearch系列---结构化搜索
概要 结构化搜索针对日期.时间.数字等结构化数据的搜索,它们有自己的格式,我们可以对它们进行范围,比较大小等逻辑操作,这些逻辑操作得到的结果非黑即白,要么符合条件在结果集里,要么不符合条件在结果集之外 ...
- Elasticsearch 结构化搜索、keyword、Term查询
前言 Elasticsearch 中的结构化搜索,即面向数值.日期.时间.布尔等类型数据的搜索,这些数据类型格式精确,通常使用基于词项的term精确匹配或者prefix前缀匹配.本文还将新版本的&qu ...
- ElasticSearch 结构化搜索全文
1.介绍 上篇介绍了搜索结构化数据的简单应用示例,现在来探寻 全文搜索(full-text search) :怎样在全文字段中搜索到最相关的文档. 全文搜索两个最重要的方面是: 相关性(Relevan ...
随机推荐
- 05 Tensorflow中变量的初始化
打开Python Shell,输入import tensorflow as tf,然后可以执行以下代码. 1.创建一个2*3的矩阵,并让所有元素的值为0.(类型为tf.float) a = tf.ze ...
- 开源任务调度平台elastic-job-lite源码解析
前段时间写过一遍文章<一文揭秘定时任务调度框架quartz>,有读者建议我再讲讲elastic-job这个任务调度框架,年末没有那么忙,就来学习一下elastic-job. 首先一点,el ...
- ZAB协议简介
Zookeeper 使用 Zookeeper Atomic Broadcast (ZAB) 协议来保障分布式数据一致性. ZAB是一种支持崩溃恢复的消息广播协议,采用类似2PC的广播模式保证正常运行时 ...
- ZooKeeper系列(5):ZooKeeper的日志和快照
ZooKeeper系列文章:https://www.cnblogs.com/f-ck-need-u/p/7576137.html#zk ZooKeeper有两种日志.一种快照.日志分为事务日志和Zoo ...
- 基于SpringMVC+Spring+MyBatis实现秒杀系统【客户端交互】
前言 该篇主要实现客户端和服务的交互.在第一篇概况里我已经贴出了业务场景的交互图片. 客户端交互主要放在seckill.js里来实现.页面展现基于jsp+jstl来实现. 准备工作 1.配置web.x ...
- Hyperledger Fabric链码之二
上篇文章中我们介绍了链码的概念,本文中我们将介绍Fabric下链码的编写和测试.我们会通过一个简单例子的方式来阐述链码API的使用. 链码API 每一个链码程序都必须实现一个接口Chainco ...
- NTP时间服务器实战应用详解-技术流ken
简介 在搭建集群服务中,要保证各节点时间一致,NTP时间服务器就成为了一个好帮手了. 系统环境 系统版本:centos6.7 服务器IP:10.220..5.166/24 客户端IP:10.220.5 ...
- npoi实现数据导出Excel
npoi .NET第三方的Office功能组件. 链接地址 http://npoi.codeplex.com/ 引用命名空间 using NPOI.HSSF.UserModel; using ...
- 解决MyEclipse中install new software问题
eclipse中点击help可以直接找到install new software选项进行安装插件,但是在Myeclipse中help没有这个选项,如下提供几种解决方法 Windows-preferen ...
- Unix awk的流程控制BEGIN和END的讲解
你可能对Unix比较熟悉,但你可能对Unix awk很陌生,这一点也不奇怪,的确,与其优秀的功能相比,awk还远没达到它应有的知名度. 流程控制语句是任何程序设计语言都不能缺少的部分.任何好的语言都有 ...