首先从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搜索(一)的更多相关文章

  1. 一次 ElasticSearch 搜索优化

    一次 ElasticSearch 搜索优化 1. 环境 ES6.3.2,索引名称 user_v1,5个主分片,每个分片一个副本.分片基本都在11GB左右,GET _cat/shards/user 一共 ...

  2. ElasticSearch搜索介绍四

    ElasticSearch搜索 最基础的搜索: curl -XGET http://localhost:9200/_search 返回的结果为: { "took": 2, &quo ...

  3. Elasticsearch搜索结果返回不一致问题

    一.背景 这周在使用Elasticsearch搜索的时候遇到一个,对于同一个搜索请求,会出现top50返回结果和排序不一致的问题.那么为什么会出现这样的问题? 后来通过百度和google,发现这是因为 ...

  4. ElasticStack学习(六):ElasticSearch搜索初探

    一.ElasticSearch搜索介绍 1.ElasticSearch搜索方式主要分为以下两种: 1).URI Search:此种查询主要是使用Http的Get方法,在URL中使用查询参数进行查询: ...

  5. Elasticsearch搜索调优权威指南 (2/3)

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/AAkVdzmkgdBisuQZldsnvg 英文原文:https://qbox.io/blog/el ...

  6. Elasticsearch搜索调优权威指南 (1/3)

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/qwkZKLb_ghmlwrqMkqlb7Q英文原文:https://qbox.io/blog/ela ...

  7. kotlin + springboot启用elasticsearch搜索

    参考自: http://how2j.cn/k/search-engine/search-engine-springboot/1791.html?p=78908 工具版本: elasticsearch ...

  8. Elasticsearch 搜索API

    章节 Elasticsearch 基本概念 Elasticsearch 安装 Elasticsearch 使用集群 Elasticsearch 健康检查 Elasticsearch 列出索引 Elas ...

  9. Elasticsearch 搜索数据

    章节 Elasticsearch 基本概念 Elasticsearch 安装 Elasticsearch 使用集群 Elasticsearch 健康检查 Elasticsearch 列出索引 Elas ...

  10. Elasticsearch搜索资料汇总

    Elasticsearch 简介 Elasticsearch(ES)是一个基于Lucene 构建的开源分布式搜索分析引擎,可以近实时的索引.检索数据.具备高可靠.易使用.社区活跃等特点,在全文检索.日 ...

随机推荐

  1. 学习ASP.NET Core Razor 编程系列十五——文件上传功能(三)

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  2. 痞子衡嵌入式:极易上手的可视化wxPython GUI构建工具(wxFormBuilder)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是wxPython GUI构建工具wxFormBuilder. 一.手工代码布局GUI界面的烦恼 如果你曾经设计过上位机软件GUI界面,初 ...

  3. SLAM+语音机器人DIY系列:(二)ROS入门——8.理解roslaunch在大型项目中的作用

    摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...

  4. Java运行时环境---ClassLoader类加载机制

    背景:听说ClassLoader类加载机制是进入BAT的必经之路. ClassLoader总述: 普通的Java开发其实用到ClassLoader的地方并不多,但是理解透彻ClassLoader类的加 ...

  5. Flask实战第3天:url_for使用

    我们之前是通过url来找到对应的视图函数 /     =>    hello_world 那么url_for则是通过视图函数找到url hello world  =>  / 演示如下 #c ...

  6. vue 单文件组件中样式加载

    在写单文件组件时,一般都是把标签.脚本.样式写到一起,这样写个人感觉有点不够简洁,所以就想着把样式分离出去. 采用import加载样式 在局部作用域(scoped)采用@import加载进来的样式文件 ...

  7. python中的zip()函数和map()函数

    一.zip()函数 1.语法: zip(iterable, ...) 参数说明: iterable,...-- 一个或多个迭代器; 在python2中: zip() 函数用于将可迭代的对象作为参数,将 ...

  8. Hacking Bsides Vancouver 2018 walkthrough

    概述: Name: BSides Vancouver: 2018 (Workshop) Date release: 21 Mar 2018 Author: abatchy Series: BSides ...

  9. pthread小结

    参考1 https://computing.llnl.gov/tutorials/pthreads/ 参考2 http://man7.org/linux/man-pages/man7/pthreads ...

  10. dede首页、列表页调用非缩略图

    在include/extend.func.php末尾添加 function firstimg($str_pic) { $str_sub=substr($str_pic,0,-7).strrchr($s ...