首先从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. python小技巧01递归解释内嵌

    现假设有一份机器人配件名单 list[头部,躯干,肢体] 头部这个list又有鼻子眼睛嘴巴这些小零件 肢体这个list有胳膊,肩膀,手.手这个list又有3种手指 所以这个list详细写出是: lis ...

  2. C# 委托链(多播委托)

    委托既可以封装一个方法,又可以对同一类型的方法进行封装,它就是多播委托 using System; using System.Collections.Generic; using System.Lin ...

  3. redis的持久化方式RDB和AOF的区别

    1.前言 最近在项目中使用到Redis做缓存,方便多个业务进程之间共享数据.由于Redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能, ...

  4. int-Integer-String之间的转换方式

    1.int和Integer之间的转换:   1) int----->Integer ①自动装箱 ②Integer的构造方法 ③调用Integer的静态方法:static Integer valu ...

  5. HeadFirst设计模式读书笔记之工厂模式

    1. 简单工厂 1. 你开了一家披萨店,点披萨的方法可能是这样: public Pizza orderPizza(String type) { Pizza pizza; if (type.equals ...

  6. 程序员50题(JS版本)(七)

    程序31:有一个已经排好序的数组.现输入一个数,要求按原来的规律将它插入数组中 var test=[213,134,134,84,62,11]; const num=33; test.push(num ...

  7. 用Python将绝对URL替换成相对URL的代码

    下面的内容内容是关于用Python将绝对URL替换成相对URL的内容,应该是对码农有些用途. #!/usr/bin/env python### author : cold night# email : ...

  8. linux下mysql区分大小写的内容

    1.数据库名严格区分大小写2.表名严格区分大小写的3.表的别名严格区分大小写4.变量名严格区分大小写5.列名在所有的情况下均忽略大小写6.列的别名在所有的情况下均忽略大小写

  9. C#零基础入门-2-Visual Studio (VS)程序初始化及各组成部分

    X:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe 可以使用桌面快捷方式启动,也可以从开始菜单启动,还 ...

  10. [20190417]隐含参数_SPIN_COUNT.txt

    [20190417]隐含参数_SPIN_COUNT.txt--//在探究latch spin计数之前,先简单探究_SPIN_COUNT.实际上oracle现在版本latch spin的数量不再是200 ...