ElasticSearch教你玩转大数据
3.3.ES交互方式① 基于RESTful APIES和所有客户端的交互都是使用JSON格式的数据.其他所有程序语言都可以使用RESTful API,通过9200端口的与ES进行通信,在开发测试阶段,你可以使用你喜欢的WEB客户端, curl命令以及火狐的POSTER插件方式和ES通信。Curl命令方式:默认windows下不支持curl命令,在资料中有curl的工具及简单使用说明。...
3.3.ES交互方式
① 基于RESTful API
ES和所有客户端的交互都是使用JSON格式的数据.
其他所有程序语言都可以使用RESTful API,通过9200端口的与ES进行通信,在开发测试阶段,你可以使用你喜欢的WEB客户端, curl命令以及火狐的POSTER插件方式和ES通信。
Curl命令方式:
默认windows下不支持curl命令,在资料中有curl的工具及简单使用说明。
② Java API
ES为Java用户提供了两种内置客户端:
节点客户端(node client):
节点客户端以无数据节点(none data node)身份加入集群,换言之,它自己不存储任何数据,但是它知道数据在集群中的具体位置,并且能够直接转发请求到对应的节点上。
传输客户端(Transport client):
这个更轻量的传输客户端能够发送请求到远程集群。它自己不加入集群,只是简单转发请求给集群中的节点。
两个Java客户端都通过9300端口与集群交互,使用ES传输协议(ES Transport Protocol)。集群中的节点
之间也通过9300端口进行通信。如果此端口未开放,你的节点将不能组成集群。
注意
Java客户端所在的ES版本必须与集群中其他节点一致,否则,它们可能互相无法识别。
3.4.扩展:Restful认识
Restful是一种面向资源的架构风格,可以简单理解为:使用URL定位资源,用HTTP动词(GET,POST,DELETE,PUT)描述操作。
GET /user/delete?id=1(不符合restful规范的写法)
DELETE /user/1
POST /user
{name:zs}
Put /user
{name:zs}
GET /user/1
使用Restful的好处:
透明性,暴露资源存在。
充分利用 HTTP 协议本身语义。
无状态,这点非常重要。在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了复杂度。
HTTP 本身提供了丰富的内容协商手段,无论是缓存,还是资源修改的乐观并发控制,都可以以业务无关的中间件来实现。
Restful的典型特征:
① Server提供的RESTful API中,URL中只使用名词来指定资源。
“资源”是REST架构或者说整个网络处理的核心。比如:
GET http://api.itsource.cn/emp/323: 获取323号员工的基本资料;
GET http://api.itsource.cn/emps: 获取源码时代所有员工资料列表;
② REST 是面向资源的,这个概念非常重要,而资源是通过 URI 进行暴露
URI 的设计只要负责把资源通过合理方式暴露出来就可以了。对资源的操作与它无关,所以REST 通过 URI 暴露资源时,会强调不要在 URI 中出现动词。
比如:左边是错误的设计,而右边是正确的
GET /rest/api/getDogs -> GET /rest/api/dogs 获取所有小狗狗
GET /rest/api/addDogs -> POST /rest/api/dogs 添加一个小狗狗
POST /rest/api/editDogs/12 -> PUT /rest/api/dogs/12 修改一个小狗狗
POST /rest/api/deleteDogs/12 -> DELETE /rest/api/dogs/12 删除一个小狗狗
左边的这种设计,很明显不符合REST风格,URI 只负责准确无误的暴露资源,而 getDogs/addDogs…已经包含了对资源的操作,这是不对的。相反右边却满足了,它的操作是使用标准的HTTP动词来体现。
③ 用HTTP协议里的动词来实现资源的添加,修改,删除等操作。
即通过HTTP动词来实现资源的状态扭转:
GET 用来获取资源,
POST 用来新建资源(也可以用于更新资源),
PUT 用来更新资源,
DELETE 用来删除资源。
比如:
GET http://api.it.cn/emp/323
POST http://api.it.cn/emp: 添加一个员工
PUT http://api.it.cn/emp/232: 更新232号员工资料
DELETE http://api.it.cn/emp/323: 删除323号员工
3.5.辅助管理工具Kibana5
① Kibana5.2.2下载地址:https://www.elastic.co/downloads/kibana
② 解压并编辑config/kibana.yml,设置elasticsearch.url的值为已启动的ES
③ 启动Kibana5 : bin\kibana.bat
④ 默认访问地址:http://localhost:5601
Discover:可视化查询分析器
Visualize:统计分析图表
Dashboard:自定义主面板(添加图表)
Timelion:Timelion是一个kibana时间序列展示组件(暂时不用)
Dev Tools :Console(同CURL/POSTER,操作ES代码工具,代码提示,很方便)
Management:管理索引库(index)、已保存的搜索和可视化结果(save objects)、设置 kibana 服务器属性。
3.6.小结
本节的重点是对Resultful的认识以及服务管理工具Kibana5的基本使用。
ES数据管理
3.7.什么是ES中的文档
Document(List) Field {name value store…}
ES是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在ES中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。
ES使用Javascript对象符号(JavaScript Object Notation),也就是JSON,作为文档序列化格式。JSON现在已经被大多语言所支持,而且已经成为NoSQL领域的标准格式。
ES存储的一个员工文档的格式示例:
{
_index : “crm”,
_type : “user”,
_id : 1,
_source : {
“email”: "nxh@itsource.cn",
“name”: “倪先华”,
“info”: {
“addr”: “四川省成都市”,
“age”: 30,
“interests”: [ “美食”, “美女” ]
},
“join_date”: “2016-06-01”
}
}
尽管原始的 employee对象很复杂,但它的结构和对象的含义已经被完整的体现在JSON中了,在ES中将对象转化为JSON并做索引要比在表结构中做相同的事情简单的多。
一个文档不只有数据。它还包含元数据(metadata)—关于文档的信息。三个必须的元数据节点是:
_index:索引库,类似于关系型数据库里的“数据库”—它是我们存储和索引关联数据的地方。
_type:在应用中,我们使用对象表示一些“事物”,例如一个用户、一篇博客、一个评论,或者一封邮件。可以是大写或小写,不能包含下划线或逗号。我们将使用 employee 做为类型名。
_id:与 _index 和 _type 组合时,就可以在ELasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义 _id ,也可以让Elasticsearch帮你自动生成。
另外还包括:_uid文档唯一标识(_type#_id)
_source:文档原始数据
_all:所有字段的连接字符串
3.8.文档的增删改
我们以员工对象为例,我们首先要做的是存储员工数据,每个文档代表一个员工。在ES中存储数据的行为就叫做索引(indexing),文档归属于一种类型(type),而这些类型存在于索引(index)中,我们可以简单的对比传统数据库和ES的对应关系:
关系数据库(MYSQL) -> 数据库DB-> 表TABLE-> 行ROW-> 列Column
Elasticsearch -> 索引库Indices -> 类型Types -> 文档Documents -> 字段Fields
ES集群可以包含多个索引(indices)(数据库),每一个索引库中可以包含多个类型(types)(表),每一个类型包含多个文档(documents)(行),然后每个文档包含多个字段(Fields)(列)。
创建索引文档
①使用自己的ID创建:
PUT {index}/{type}/{id}
{
“field”: “value”,
…
}
②ES内置ID创建:
POST {index}/{type}/
{
“field”: “value”,
…
}
①②ES响应内容:
{
“_index”: “itsource”,
“_type”: “employee”,
“_id”: xxxxxx,
“_version”: 1, //文档版本号
“created”: true //是否新增
}
③ 获取指定ID的文档
GET it/employee/123?pretty
③返回的内容:
{
“_index” : “it”,
“_type” : “employee”,
“_id” : “123”,
“_version” : 1,
“found” : true,
“_source” : {
“email”: "nxh@itsource.cn",
“fullName”: “倪先华”,
…
“joine_date”: “2016-06-01”
}
}
返回文档的部分字段:
GET默认返回整个文档,通过GET /itsource/employee/123?_source=fullName,email
只返回文档内容,不要元数据:
GET it/employee/123/_source
只检查文档是否存在(查询头信息):
curl -i -X HEAD http://localhost:9200/itsource/employee/123
④ 修改文档
更新整个文档
同PUT {index}/{type}/{id}
在响应中,我们可以看到Elasticsearch把 _version 增加了。
{
…
“_version” : 2,
“created”: false
}
created 标识为 false 因为同索引、同类型下已经存在同ID的文档。
在内部,Elasticsearch已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。Elasticsearch会在你继续索引更多数据时清理被删除的文档。
局部更新文档
接受一个局部文档参数 doc,它会合并到现有文档中,对象合并在一起,存在的标量字段被覆盖,新字段被添加。
POST itsource/employee/123/_update
{
“doc” : {
“email” : "nixianhua@it.cn",
“salary”: 1000
}
}
email会被更新覆盖,salary会新增。
这个API 似乎 允许你修改文档的局部,但事实上Elasticsearch
遵循与之前所说完全相同的过程,这个过程如下:
- 从旧文档中检索JSON
- 修改它
- 删除旧文档
- 索引新文档
脚本更新文档
也可以通过使用简单的脚本来进行。这个例子使用一个脚本将age加5:
POST it/emploee/123/_update
{
“script” : “ctx._source.age += 5”
}
在上面的例子中, ctx._source指向当前被更新的文档。
注意,目前的更新操作只能一次应用在一个文档上。
删除文档
DELETE {index}/{type}/{id}
存在文档的返回:
{
“found” : true,
“_index” : “website”,
“_type” : “blog”,
“_id” : “123”,
“_version” : 3
}
不存在的返回:
{
“found” : false,
“_index” : “website”,
“_type” : “blog”,
“_id” : “123”,
“_version” : 4
}
注意:尽管文档不存在,但_version依旧增加了。这是内部记录的一部分,它确保在多节点间不同操作可以有正确的顺序。
批量操作bulk API
使用单一请求来实现多个文档的create、index、update 或 delete。
Bulk请求体格式:
{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n
每行必须以 “\n” 符号结尾,包括最后一行。这些都是作为每行有效的分离而做的标记。
create当文档不存在时创建之。
index创建新文档或替换已有文档。
update局部更新文档。
delete删除一个文档。
例如:
POST _bulk
{ “delete”: { “_index”: “it”, “_type”: “employee”, “_id”: “123” }}
{ “create”: { “_index”: “it”, “_type”: “blog”, “_id”: “123” }}
{ “title”: “我发布的博客” }
{ “index”: { “_index”: “it”, “_type”: “blog” }}
{ “title”: “我的第二博客” }
注意:delete后不需要请求体,最后一行要有回车
3.9.文档的简单查询
3.9.1.通过文档ID获取
略
3.9.2.批量获取
mget API参数是一个 docs数组,数组的每个节点定义一个文档的 _index 、 _type 、 _id 元数据。如果你只想检索一个或几个确定的字段,也可以定义一个 _source 参数:
方式1:GET _mget
{
“docs” : [
{
“_index” : “it”,
“_type” : “blog”,
“_id” : 2
},
{
“_index” : “it”,
“_type” : “employee”,
“_id” : 1,
“_source”: [“name”,“age”]
}
]
}
方式2:同一个索引库的同一个类型下
GET it/blog/_mget
{
“ids” : [ “2”, “1” ]
}
3.9.3.空搜索
没有指定任何的查询条件,只返回集群索引中的所有文档: GET _search
3.9.4.分页搜索
和SQL使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch接受 from 和 size 参数:
size : 每页条数,默认 10
from : 跳过开始的结果数,默认 0
如果你想每页显示5个结果,页码从1到3,那请求如下:
GET _search?size=5
GET _search?size=5&from=5
GET _search?size=5&from=10
3.9.5.查询字符串搜索
一个搜索可以用纯粹的uri来执行查询。在这种模式下使用搜索,并不是所有的选项都是暴露的。它可以方便快速进行 curl 测试。
查询年龄为25岁的员工
GET it/employee/_search?q=age:25
如果q后的参数不指定Fileds则默认查询_all字段(隐含的文档所有字段的连接内容)
类似的查询语法参考lucene,如:
+name:john +tweet:mary
+name:(mary john) +date:>2014-09-10 +(aggregations geo)
age[20 TO 30]
4.DSL查询与过滤
4.1.什么是DSL查询
由ES提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询。
DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现。我们可以这样表示之前关于“倪先华”的查询:
查询字符串模式:GET itsource/employee/_search?q=fullName:倪先华
DSL模式:
GET itsource/employee/_search
{
“query” : {
“match” : {
“fullName” : “倪先华”
}
}
}
对于简单查询,使用查询字符串比较好,但是对于复杂查询,由于条件多,逻辑嵌套复杂,查询字符串不易组织与表达,且容易出错,因此推荐复杂查询通过DSL使用JSON内容格式的请求体代替。
4.2.DSL查询
使用DSL查询,必须要传递query参数给ES。
GET _search
{“query”: YOUR_QUERY_HERE}
一个常用的相对完整的DSL查询:
GET itsource/employee/_search
{
“query”: {
“match”: {“sex”:“女”}
},
“from”: 20,
“size”: 10,
" _source": [“fullName”, “age”, “email”],
“sort”: [{“join_date”: “desc”},{“age”: “asc”}]
}
上面的DSL查询语句代表:查询公司员工性别为女的员工,并按照加入时间降序、年龄升序排列,最终返回第21条至30条数据(只返回名字、年龄和email字段)
4.3.DSL过滤
DSL过滤语句和DSL查询语句非常相似,但是它们的使用目的却不同:
DSL过滤查询文档的方式更像是对于我的条件“有”或者“没有”,而DSL查询语句则像是“有多像”。
DSL过滤和DSL查询在性能上的区别:
过滤结果可以缓存并应用到后续请求。
查询语句同时匹配文档,计算相关性,所以更耗时,且不缓存。
过滤语句可有效地配合查询语句完成文档过滤。
原则上,使用DSL查询做全文本搜索或其他需要进行相关性评分的场景,其它全用DSL过滤。
2.0以上的用法
{
“query”: {
“bool”: {
“must”: [
{“match”: {“description”: “search” }}
],
“filter”: {
“term”: {“tags”: “lucene”}
}
}
}
}
2.0以前的用法
{
“query”: {
“filtered”: {
“query”: {
“match”: {“description”: “search” }
},
“filter”: {
“term”: {“tags”: “lucene”}
}
}
}
}
4.4.使用DSL查询与过滤
① 全匹配(match_all)
普通搜索(匹配所有文档):
{
“query” : {
“match_all” : {}
}
}
如果需要使用过滤条件(在所有文档中过滤,红色部分默认可不写):
{
“query” : {
“bool” : {
“must” : [{
“match_all”:{}
}],
“filter”:{…}
}
}
}
② 标准查询(match和multi_match)
match查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
如果你使用match查询一个全文本字段,它会在真正查询之前用分析器先分析查询字符:
{
“query”: {
“match”: {
“fullName”: “Steven King”
}
}
}
上面的搜索会对Steven King分词,并找到包含Steven或King的文档,然后给出排序分值。
如果用 match 下指定了一个确切值,在遇到数字,日期,布尔值或者 not_analyzed的字符串时,它将为你搜索你给定的值,如:
{ “match”: { “age”: 26 }}
{ “match”: { “date”: “2014-09-01” }}
{ “match”: { “public”: true }}
{ “match”: { “tag”: “full_text” }}
multi_match 查询允许你做 match查询的基础上同时搜索多个字段:
{
“query”:{
“multi_match”: {
“query”: “Steven King”,
“fields”: [ “fullName”, “title” ]
}
}
}
上面的搜索同时在fullName和title字段中匹配。
提示:match一般只用于全文字段的匹配与查询,一般不用于过滤。
③单词搜索与过滤(Term和Terms)
{
“query”: {
“bool”: {
“must”: {
“match_all”: {}
},
“filter”: {
“term”: {
“tags”: “elasticsearch”
}
}
}
}
}
Terms搜索与过滤
{
“query”: {
“terms”: {
“tags”: [“jvm”, “hadoop”, “lucene”],
“minimum_match”: 2
}
}
}
minimum_match:至少匹配个数,默认为1
④ 组合条件搜索与过滤(Bool)
组合搜索bool可以组合多个查询条件为一个查询对象,查询条件包括must、should和must_not。
例如:查询爱好有美女,同时也有喜欢游戏或运动,且出生于1990-06-30及之后的人。
{
“query”: {
“bool”: {
“must”: [{“term”: {“hobby”: “美女”}}],
“should”: [{“term”: {“hobby”: “游戏”}},
{“term”: {“hobby”: “运动”}}
],
“must_not”: [
{“range” :{“birth_date”:{“g t”: “1990-06-30”}}}
],
“filter”: […],
“minimum_should_match”: 1
}
}
}
提示: 如果 bool 查询下没有must子句,那至少应该有一个should子句。但是 如果有 must子句,那么没有 should子句也可以进行查询。
⑤ 范围查询与过滤(range)
range过滤允许我们按照指定范围查找一批数据:
{
“query”:{
“range”: {
“age”: {
“gte”: 20,
“lt”: 30
}
}
}
}
上例中查询年龄大于等于20并且小于30。
gt:> gte:>= lt:< lte:<=
⑥ 存在和缺失过滤器(exists和missing)
{
“query”: {
“bool”: {
“must”: [{
“match_all”: {}
}],
“filter”: {
“exists”: { “field”: “gps” }
}
}
}
}
提示:exists和missing只能用于过滤结果。
⑦ 前匹配搜索与过滤(prefix)
和term查询相似,前匹配搜索不是精确匹配,而是类似于SQL中的like ‘key%’
{
“query”: {
“prefix”: {
“fullName”: “倪”
}
}
}
上例即查询姓倪的所有人。
⑧ 通配符搜索(wildcard)
使用代表0~N个,使用?代表1个。
{
“query”: {
“wildcard”: {
“fullName”: "倪华"
}
}
}
4.5.小结
DSL查询是ES提供的通用查询方式,这种方式最大的特点是开发语言的无关性,即任意的客户端只要支持HTTP请求,就可以通过JSON格式的查询数据完成复杂的搜索。
查询与过滤在实际的项目开发中是经常遇到的主题。
5.分词与映射
5.1.为什么要使用分词与映射
在全文检索理论中,文档的查询是通过关键字查询文档索引来进行匹配,因此将文本拆分为有意义的单词,对于搜索结果的准确性至关重要,因此,在建立索引的过程中和分析搜索语句的过程中都需要对文本串分词。
ES中分词需要对具体字段指定分词器等细节,因此需要在文档的映射中明确指出。
5.2.IK分词器
ES默认对英文文本的分词器支持较好,但和lucene一样,如果需要对中文进行全文检索,那么需要使用中文分词器,同lucene一样,在使用中文全文检索前,需要集成IK分词器。
ES的IK分词器插件源码地址:https://github.com/medcl/elasticsearch-analysis-ik
① Maven打包IK插件
② 解压target/releases/elasticsearch-analysis-ik-5.2.2.zip文件
并将其内容放置于ES根目录/plugins/ik
③ 配置插件:(可默认不改)
插件配置:plugin-descriptor.properties
④ 分词器(可默认不改)
词典配置:config/IKAnalyzer.cfg.xml
⑤ 重启ES
⑥ 测试分词器
POST _analyze
{
“analyzer”:“ik_smart”,
“text”:“中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首”
}
注意:IK分词器有两种类型,分别是ik_smart分词器和ik_max_word分词器。
ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。
ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;
提示:也可以直接使用已集成好各种插件的elasticsearch-rtf-master.zip中文发行版,但ES版本为5.1.1。
5.3.文档映射Mapper
ES的文档映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型。
5.3.1.ES字段类型
① 基本字段类型
字符串:text,keyword
text默认为全文文本,keyword默认为非全文文本
数字:long,integer,short,double,float
日期:date
逻辑:boolean
② 复杂数据类型
对象类型:object
数组类型:array
地理位置:geo_point,geo_shape
5.3.2.默认映射
查看索引类型的映射配置:GET {indexName}/{typeName}/_mapping
ES在没有配置Mapping的情况下新增文档,ES会尝试对字段类型进行猜测,并动态生成字段和类型的映射关系。
JSON type Field type
Boolean: true or false “boolean”
Whole number: 123 “long”
Floating point: 123.45 “float”
String, valid date:“2014-09-15” “date”
String: “foo bar” “text”
5.3.3.简单类型映射
字段映射的常用属性配置列表
type 类型:基本数据类型,integer,long,date,boolean,keyword,text…
enable 是否启用:默认为true。 false:不能索引、不能搜索过滤,仅在_source中存储
boost 权重提升倍数:用于查询时加权计算最终的得分。
format 格式:一般用于指定日期格式,如 yyyy-MM-dd HH:mm:ss.SSS
ignore_above 长度限制:长度大于该值的字符串将不会被索引和存储。
ignore_malformed 转换错误忽略:true代表当格式转换错误时,忽略该值,被忽略后不会被存储和索引。
include_in_all 是否将该字段值组合到_all中。
_all : 虚拟字段,每个文档都有该字段,代表所有字段的组合信息,作用方便直接对整个文档的所有信息进行搜索。
{id:1,name:zs,age:20,_all:1,zs,20}
null_value 默认控制替换值。如空字符串替换为”NULL”,空数字替换为-1
store 是否存储:默认为false。true意义不大,因为_source中已有数据
index 索引模式:analyzed (索引并分词,text默认模式), not_analyzed (索引不分词,keyword默认模式),no(不索引)
analyzer 索引分词器:索引创建时使用的分词器,如ik_smart,ik_max_word,standard
search_analyzer 搜索分词器:搜索该字段的值时,传入的查询内容的分词器。
fields 多字段索引:当对该字段需要使用多种索引模式时使用。
如:城市搜索New York
“city”: {
“type”: “text”,
“analyzer”: “ik_smart”,
“fields”: {
“raw”: {
“type”: “keyword”
}
}
}
那么以后搜索过滤和排序就可以使用city.raw字段名
① 针对单个类型的映射配置方式
POST {indexName}/{typeName}/_mapping
{
“{typeName}”: {
“properties”: {
“id”: {
“type”: “long”
},
“content”: {
“type”: “text”,
“analyzer”: “ik_smart”,
“search_analyzer”: “ik_smart”
}
}
}
}
注意:你可以在第一次创建索引的时候指定映射的类型。此外,你也可以晚些时候为新字段添加映射(或者为已有的类型更新映射)。
你可以向已有映射中增加字段,但你不能修改它。如果一个字段在映射中已经存在,这可能意味着那个字段的数据已经被索引。如果你改变了字段映射,那已经被索引的数据将错误并且不能被正确的搜索到。
我们可以更新一个映射来增加一个新字段,但是不能把已有字段的类型那个从 analyzed 改到 not_analyzed。
② 同时对多个类型的映射配置方式(推荐)
PUT {indexName}
{
“mappings”: {
“user”: {
“properties”: {
“id”: {
“type”: “integer”
},
“info”: {
“type”: “text”,
“analyzer”: “ik_smart”,
“search_analyzer”
}
}
},
“dept”: {
“properties”: {
“id”: {
“type”: “integer”
},
…更多字段映射配置
}
}
}
}
5.3.4.对象及数组类型映射
① 对象的映射与索引
{
“id” : 1,
“girl” : {
“name” : “王小花”,
“age” : 22
}
}
对应的mapping配置:
{
“properties”: {
“id”: {“type”: “long”},
“girl”: {
“properties”:{
“name”: {“type”: “keyword”},
“age”: {“type”: “long”}
}
}
}
}
注意:Lucene不理解内置对象,一个lucene文档包含键值对的一个扁平化列表,以便于ES索引内置对象,它把文档转换为类似这样:
{
“id”: 1,
“girl.name”:“王小花”,
“girl.age”:26
}
内置字段与名字相关,区分两个字段中相同的名字,可以使用全路径,例如user.girl.name
② 数组与对象数组
注意:数组中元素的类型必须一致。
Java : Object[] objects = [];
Js : var objs = [1,true,”haha”];
{
“id” : 1,
“hobby” : [“王小花”,“林志玲”]
}
对应的mapping配置是:
{
“properties”: {
“id”: {“type”: “long”},
“hobby”: {“type”: “keyword”}
}
}
对象数组的映射
{
“id” : 1,
“girl”:[{“name”:“林志玲”,“age”:32},{“name”:“赵丽颖”,“age”:22}]
}
对应的映射配置为:
“properties”: {
“id”: {
“type”: “long”
},
“girl”: {
“properties”: {
“age”: { “type”: “long” },
“name”: { “type”: “text” }
}
}
}
注意:同内联对象一样,对象数组也会被扁平化索引
{
“user.girl.age”: [32, 22],
“user.girl.name”: [“林志玲”, “赵丽颖”]
}
注意:扁平化后,对象属性的相关性已经丢失,因为每个多值字段只是一个数值集,不是排序的数组。
比如查询:哪个女朋友的年龄是22岁? 这个是无法查询到答案,如果需要保留关系,需要使用嵌套对象nested objects。
5.3.5.全局映射
全局映射可以通过动态模板和默认设置两种方式实现。
默认方式:default
索引下所有的类型映射配置会继承_default_的配置,如:
PUT {indexName}
{
“mappings”: {
“default”: {
“_all”: {
“enabled”: false
}
},
“user”: {},
“dept”: {
“_all”: {
“enabled”: true
}
}
}
}
上例中:user和dept都会继承_default_的配置,user类型的文档中将不会合并所有字段到_all,而dept会。
动态模板:dynamic_templates
注意:ES会默认把string类型的字段映射为text类型(默认使用标准分词器)和对应的keyword类型,如:
“name”: {
“type”: “text”,
“fields”: {
“keyword”: {
“type”: “keyword”,
“ignore_above”: 256
}
}
}
在实际应用场景中,一个对象的属性中,需要全文检索的字段较少,大部分字符串不需要分词,因此,需要利用全局模板覆盖自带的默认模板:
PUT _template/global_template //创建/修改名为global_template的模板
{
“template”: “", //匹配所有索引库
“settings”: { “number_of_shards”: 1 }, //匹配到的索引库只创建1个主分片
“mappings”: {
“default”: {
“_all”: {
“enabled”: false //关闭所有类型的_all字段
},
“dynamic_templates”: [
{
“string_as_text”: {
“match_mapping_type”: “string”,//匹配类型string
“match”: "_txt”, //匹配字段名字以_txt结尾
“mapping”: {
“type”: “text”,//将类型为string的字段映射为text类型
“analyzer”: “ik_max_word”,
“search_analyzer”: “ik_max_word”,
“fields”: {
“raw”: {
“type”: “keyword”,
“ignore_above”: 256
}
}
}
}
},
{
“string_as_keyword”: {
“match_mapping_type”: “string”,//匹配类型string
“mapping”: {
“type”: “keyword”//将类型为string的字段映射为keyword类型
}
}
}
]
}
}}
说明:上例中定义了两种动态映射模板string_as_text和string_as_keyword.
在实际的类型字段映射时,会依次匹配:
①字段自定义配置、②全局dynamic_templates[string_as_text、string_as_keyword]、
③索引dynamic_templates[…]、④ES自带的类型映射。 以最先匹配上的为准。
注意:索引库在创建的时候会继承当前最新的dynamic_templates,索引库创建后,修改动态模板,无法应用到已存在的索引库。
5.3.6.最佳实践
映射的配置会影响到后续数据的索引过程,因此,在实际项目中应遵循如下顺序规则:
① 配置全局动态模板映射(覆盖默认的string映射)
② 配置自定义字段映射(由于基本类型主要用于过滤和普通查询,因此,字段映射主要对需要全文检索的字段进行配置)
③ 创建、更新和删除文档
④ 搜索
更多推荐
所有评论(0)