ElasticSearch搜索(一)
首先从ES的支持的字段说起,ES文档中字段有多种类型 官方文档。
这几个比较常用:
text,keyword,integer,float,boolean,object,geo_point(地理坐标),geo_shape(描述地理区域),date.
注:不要以为date只能表示 2015-01-01 这种类型,2015/01/01 12:10:30这种类型也一样可以,不像MySQL里面时间还分很多种细分的类型,ES就一个date类型。
注意:这里没有列出array,在ES中,array不是一种单独的类型,但是你可以往ES里面存数组,这个地方有点难以理解,
举个例子: 文档里面我要定义一个字段叫 friends ,用来存储用户的朋友列表,
用 text 类型定义字段:
'friends' => [
'type' => 'text'
]
看似这仅仅定义了一个text类型的字段,并不是我们想要的数组,重点解释来了,虽然我们的friends是字符串类型,但是
我们在存入数据的时候 往 friends里面存储两个或者三个字符串,他就变成数组了!
其实这句话描述还是不准确,不是从字符串变成数组,而是多个字符串组成了一个数组!
插入数据:
$this->putDoc([
'first_name' => $this->faker->name,
'last_name' => $this->faker->name,
'age' => $this->faker->numberBetween(20,80),
'height' => (float)($this->faker->numberBetween(160,200)/100),
'friends' => [
$this->faker->name(),
$this->faker->name(),
$this->faker->name(),
$this->faker->name()
]
]);
这个putDoc来得有点突然是不是?因为这是延续上一篇文章的续集,请看上一篇文章 使用PHP操作ElasticSearch。
注意:faker是用来随机生成数据的,详细信息参考谷歌。
你看,friends明明是 text 类型,但是我在插入的时候插入了多条数据,他变成了一个字符串类型的集合,前面我说他是数组,这个地方
我把他说成是集合,这回更准确了,因为数组在ES中查询是不能保证顺序的,所以集合更准确,官方文档中也表示他更像集合
再说一下object,模板里面这样定义:
'info' => [
'type' => 'object',
'properties' => [
'country' => [
'type' => 'text',
'analyzer' => 'ik_max_word'
],
'sex' => [
'type' => 'keyword'
]
]
]
这里定义了一个对象文档,指定了下面两个属性的基本信息,但是不代表这个对象就只能存储两个属性,
比如我还可以在添加文档的时候往里面添加一个skin 肤色的字段,完全没有问题,只不过这里定义的两个
字段我们设置了类型和具体的analyzer,没有在这里定义,但是我们实际上添加了的字段比如skin,ES会
自动设置正确的类型,以及默认的analyzer.
存入数据:
'info' => [
'country' => ['中国','印度','法国','英国','瑞士','刚果共和国'][random_int(0,5)],
'sex' => ['男','女'][random_int(0,1)],
'skin' => ['白','黑','黄'][random_int(0,2)],
]
还有一个keyword,他和text都表示字符串,区别在于 keyword里面的值不会被分词器分词,text里面的值会被分词器智能拆分,
记住这一点,这一点非常重要,后面还会讲到这个区别。
在定义text字段的时候 analyzer和index你需要清楚的地方:
'last_name' => [
'type' => 'text',
//'analyzer' => 'standard', // 这个地方不设置analyzer会默认standard
//'index' => false
]
analyzer不设置analyzer会默认standard
对于老版本的 ES,这里的index允许设置为 analyzed/not_analyzed/no,
大部分网络上的文章都是这样讲的,但是,最新版本已经移除了这些选项,
现在只能是 true或false,所以我建议当你有一点基础后,通读一下官方最新文档,虽然是英文的
如果这里设置为false,这个字段不加入索引,不能在查询条件中出现,默认为true
等一下,这里突然发现有点不对劲,以前可以设置 分析/不分析/不索引,现在只能设置索引和不索引了,
如果想实现索引且不分析,那keyword类型刚好符合,而text字段是为分析而生的。
ES中的搜索分两个概念,匹配和过滤
匹配通常针对的是 text 类型的字段,而过滤针通常对的精确的类型,比如 integer,keyword,date等,
之所以加了通常二字,说明他们之间没有明确的规定,匹配可以去匹配data,integer等类型,过滤也可以去过滤text字段,
通过匹配的方式去找精确类型通常不会出现什么问题,通过过滤去找text类型的数据通常会得到意外的结果。
谨记:如果没有特殊情况,匹配针对text类型,过滤针对其他类型,
但是对于精确类型使用过滤的性能通常比匹配更高,所以能使用过滤的地方都过滤。
注意:这里要区别一下MySQL中的过滤概念,MySQL中的过滤是对查找后的数据进行过滤,而在ES中,过滤和匹配都等同于MySQL中的查找,
匹配适合查找模糊数据,过滤适合查找精确数据而已。
为了简化代码,下面的搜索都基于一下这份代码,更改的部分只是 $query:
$params = [
'index' => $this->index,
'type' => $this->type,
'body' => array_merge([
'from' => $from,
'size' => $size
],$query)
];
常用的过滤:
term(精确查找)
查找倪玲为44的数据
$query = [
'query' => [
'term' => [
'age' => 44
]
]
];
terms(精确查找多个字段)
查找年龄为 44或55或66的数据
$query = [
'query' => [
'terms' => [
'age' => [44,55,66]
]
]
];
range(范围查找),
$query = [
'query' => [
'range' => [
'age' => [
'gt' => 43,
'lt' => 45
]
]
]
];
exists(等同于MySQL中的 is not null),
查找存在age属性的文档
$query = [
'query' => [
'exists' => [
'field' => 'age'
]
]
];
missing(等同于 MySQL中的 is null),
注意:这个过滤方法在2.x版本就废弃了,请使用 must_not 嵌套 exists 来实现
bool(用来组合其他过滤条件,包含 must,must_not,should操作)
$query = [
'query' => [
'bool' => [
'should' => [
'range' => [
'height' => ['gt' => 1.8]
]
],
'must_not' => [
'term' => [
'info.sex' => '女'
]
],
'must' => [
[
'term' => [
'info.country' => '法国'
]
],
[
'term' => [
'info.skin' => '白'
]
]
]
]
]
];
上面这个查询的意思是,身高应该大于1.8,性别不能是女,国家是法国且肤色是黑色。
这里country实际上是text类型,但是我任然通过过滤的方法找到了正确的值,但是这种方式是非常危险的,
这里之所以找到了正确的值,是因为country类型很简单,碰巧
analyzer(这里用的ik,如果是standard就没那么好运了)没有对其进行拆分。
常用的查询:
match(匹配一个字段)
$query = [
'query' => [
'match' => [
'height' => '1.8'
]
]
];
match_all(匹配所有文档,相当于没有条件)
等于是 $query = []
multi_match(匹配多个字段)
匹配姓和名里面包含 'Riley Libby Preston' 的数据
$query = [
'query' => [
'multi_match' => [
'query' => 'Riley Libby Preston',
'fields' => ['first_name','last_name']
]
]
];
bool(用来组合其他匹配条件,包含 must,must_not,should操作)
$query = [
'query' => [
'bool' => [
'should' => [
'match' => [
'height' => '1.8'
]
],
'must_not' => [
'match' => [
'info.sex' => '男'
]
]
]
]
];
在实际使用中,匹配和过滤都是混合搭配使用的,比如:
$query = [
'query' => [
'bool' => [
'should' => [
'match' => [
'height' => '1.8'
]
],
'must_not' => [
'term' => [
'info.sex' => '女'
]
],
'must' => [
[
'match' => [
'info.country' => '法国'
]
],
[
'match' => [
'info.skin' => '白'
]
]
]
]
]
];
match时常会出现一些怪异的现象,如果你不清楚你用的analyzer,比如这个例子:
$query = [
'query' => [
'bool' => [
'must' => [
[
'match' => [
'last_name' => 'Hamill'
]
],
[
'match' => [
'info.country' => '法国'
]
]
]
]
]
];
这个查询的需求是选出last_name中匹配到Hamill并且国家匹配到法国的结果,但是查询的结果是这样的,
last_name 的中包含 Hamill,在我们意料之中,但是 country出现了英国,法国等很多国家,这个太意外了,
现在来改造一下这个 $query,很小的改造,只需要把法国改成法,再次查询,这次的结果完美的实现了我们的需求。
原因在于:
文档中的法国二字被analyzer拆分成 (法,国) 存储在索引中,同理,英国被拆分为 (英,国),
现在你搜索法国的时候,你的这个搜索词默认会被拆分成 (法,国),然后拿着这两个词去分别查找,
第一个法可以匹配所有法国,第二个国字可以匹配到英国,美国等所有包含国字的结果。
现在你知道结果的形成原因了。
这个很大程度上上取决于你使用的analyzer,不同的analyzer分词的策略不一样,所以你有必要先搞明白你用的分词器
他的大概分词策略,上面这个例子没有指定analyzer,是ES默认的分词器在起作用,当我指定analyzer为 ik_max_word后,情况
发生了变化,这个时候法国被当成了一个整体,没有被拆分。
可以通过简单的测试来看看具体分词器的分词方式:
$params = [
'body' => [
'analyzer' => 'ik_max_word', //默认 standard
'text' => '我在广场吃着炸鸡'
]
];
return $this->EsClient->indices()->analyze($params);
默认分词器standard会把这句话简单的拆分成单个字,而ik相对就更懂中文一点,拆分出来的词更有语义化,
大部分的analyzer对英文的分词都基于空格拆分
ElasticSearch搜索(一)的更多相关文章
- 一次 ElasticSearch 搜索优化
一次 ElasticSearch 搜索优化 1. 环境 ES6.3.2,索引名称 user_v1,5个主分片,每个分片一个副本.分片基本都在11GB左右,GET _cat/shards/user 一共 ...
- ElasticSearch搜索介绍四
ElasticSearch搜索 最基础的搜索: curl -XGET http://localhost:9200/_search 返回的结果为: { "took": 2, &quo ...
- Elasticsearch搜索结果返回不一致问题
一.背景 这周在使用Elasticsearch搜索的时候遇到一个,对于同一个搜索请求,会出现top50返回结果和排序不一致的问题.那么为什么会出现这样的问题? 后来通过百度和google,发现这是因为 ...
- ElasticStack学习(六):ElasticSearch搜索初探
一.ElasticSearch搜索介绍 1.ElasticSearch搜索方式主要分为以下两种: 1).URI Search:此种查询主要是使用Http的Get方法,在URL中使用查询参数进行查询: ...
- Elasticsearch搜索调优权威指南 (2/3)
本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/AAkVdzmkgdBisuQZldsnvg 英文原文:https://qbox.io/blog/el ...
- Elasticsearch搜索调优权威指南 (1/3)
本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/qwkZKLb_ghmlwrqMkqlb7Q英文原文:https://qbox.io/blog/ela ...
- kotlin + springboot启用elasticsearch搜索
参考自: http://how2j.cn/k/search-engine/search-engine-springboot/1791.html?p=78908 工具版本: elasticsearch ...
- Elasticsearch 搜索API
章节 Elasticsearch 基本概念 Elasticsearch 安装 Elasticsearch 使用集群 Elasticsearch 健康检查 Elasticsearch 列出索引 Elas ...
- Elasticsearch 搜索数据
章节 Elasticsearch 基本概念 Elasticsearch 安装 Elasticsearch 使用集群 Elasticsearch 健康检查 Elasticsearch 列出索引 Elas ...
- Elasticsearch搜索资料汇总
Elasticsearch 简介 Elasticsearch(ES)是一个基于Lucene 构建的开源分布式搜索分析引擎,可以近实时的索引.检索数据.具备高可靠.易使用.社区活跃等特点,在全文检索.日 ...
随机推荐
- 学习ASP.NET Core Razor 编程系列十五——文件上传功能(三)
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- 痞子衡嵌入式:极易上手的可视化wxPython GUI构建工具(wxFormBuilder)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是wxPython GUI构建工具wxFormBuilder. 一.手工代码布局GUI界面的烦恼 如果你曾经设计过上位机软件GUI界面,初 ...
- SLAM+语音机器人DIY系列:(二)ROS入门——8.理解roslaunch在大型项目中的作用
摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...
- Java运行时环境---ClassLoader类加载机制
背景:听说ClassLoader类加载机制是进入BAT的必经之路. ClassLoader总述: 普通的Java开发其实用到ClassLoader的地方并不多,但是理解透彻ClassLoader类的加 ...
- Flask实战第3天:url_for使用
我们之前是通过url来找到对应的视图函数 / => hello_world 那么url_for则是通过视图函数找到url hello world => / 演示如下 #c ...
- vue 单文件组件中样式加载
在写单文件组件时,一般都是把标签.脚本.样式写到一起,这样写个人感觉有点不够简洁,所以就想着把样式分离出去. 采用import加载样式 在局部作用域(scoped)采用@import加载进来的样式文件 ...
- python中的zip()函数和map()函数
一.zip()函数 1.语法: zip(iterable, ...) 参数说明: iterable,...-- 一个或多个迭代器; 在python2中: zip() 函数用于将可迭代的对象作为参数,将 ...
- Hacking Bsides Vancouver 2018 walkthrough
概述: Name: BSides Vancouver: 2018 (Workshop) Date release: 21 Mar 2018 Author: abatchy Series: BSides ...
- pthread小结
参考1 https://computing.llnl.gov/tutorials/pthreads/ 参考2 http://man7.org/linux/man-pages/man7/pthreads ...
- dede首页、列表页调用非缩略图
在include/extend.func.php末尾添加 function firstimg($str_pic) { $str_sub=substr($str_pic,0,-7).strrchr($s ...