如何使用Elasticsearch实现对动态字段的搜索
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Elasticsearch实现对动态字段的搜索相关的知识,希望对你有一定的参考价值。
参考技术A 需求如下:在Blog中保存着一串标签,标签名由用户自定义,如TagSex,其值也是可以自由输入的字符串,现需要根据这个动态的标签名和值筛选出这一篇Blog。你可能使用mysql实现一个类似Elasticsearch倒排索引一样的关联tag和blog的关联表, 但如果还需要支持其他条件的筛选+排序那想一想都觉得复杂.
那我们换成Elasticsearch来实现吧.
索引(类似mysql的表)的Mapping(类似mysql的字段)如下
其他字段就不用多说, 我们来关注tags字段, tags类型是 Object 类型, 其可以存放任意字段.
多个tag就可以存放在这个Object里面:
或是还可以支持像这样的多个value:
在搜索时, 直接使用普通的query语句即可:
当然, 事情没这么简单, 你还需要考虑这几个问题:
下面的方案2就会解决key太多的问题,不过你觉得方案1也可行的话,我们就继续来解决第二个问题:如何将动态添加的字段类型固定为keyword?
当动态添加字段的时候Elasticsearch默认会自动推断类型, 如string就会使用 text 类型存储.
如果需要修改这个逻辑就需要使用到 dynamic templates .
在这个案例中, 我们需要对动态添加的tags字段进行精确搜索, 而不是全文搜索, 所以需要使用到keyword类型, 那么就可以这样写dynamic_templates:
现在动态的向tags对象里添加字段都将作为keyword类型存储。
key的多少会不会影响ES的性能笔者也没找到资料,抱歉也太多时间去验证,有兴趣的可以自己试验一下,不过猜测这对ES的性能影响很小,但如果你觉得太多的key不美观或者担心性能,那可以使用另一种方案:
设置tags字段的类型的Array,然后将tag和value拼接起来放在tags里,在搜索时就可以使用 term-query 来查询
document:
如果一个tag有多个值,那么可以这样存储:
"id": 1,
"tags": ["tagAbc=blue", "tagAbc=green"]
其中 = 作为tagKey和value的分隔符,可根据项目需要而定。
同样在搜索的时候也需要拼接key和value:
query:
这种方案的优点是不会生成太多的key,性能稳定,麻烦的是在存储到ES之前需要先处理一次(不过实际上不算什么问题)。
https://stackoverflow.com/questions/34556585/supporting-query-on-dynamic-columns-in-elastic-search
Elasticsearch:Dynamic field mapping
当 Elasticsearch 在文档中检测到新字段时,默认情况下会动态将该字段添加到类型映射中。 dynamic 参数控制此行为。
你可以通过将 dynamic 参数设置为 true 或 runtime 来明确指示 Elasticsearch 基于传入文档动态创建字段。 启用动态字段映射后,Elasticsearch 使用下表中的规则来确定如何映射每个字段的数据类型。
这篇文章是我之前文章 “Elasticsearch:Dynamic mapping” 的一个补充。
注意:下表中的字段数据类型是 Elasticsearch 动态检测的唯一字段数据类型。 你必须显式映射所有其他数据类型。
JSON data type | "dynamic": "true" | "dynamic": "runtime" |
---|---|---|
null | 不添加任何字段 | 不添加任何字段 |
true 或者 false | boolean | boolean |
double | float | double |
long | long | long |
object | object | 不添加任何字段 |
array | 依赖于数组里的第一个非 null 值 | 依赖于数组里的第一个非 null 值 |
通过 date detection 的字符串 | date | date |
通过 numeric detection 的字符串 | float 或者 long | double 或者 long |
不通过 date detection 或者 numberic detection 的字符串 | 含有 .keyword 子字段的 text 类型 | keyword |
你可以在文档和 object 级别禁用动态映射。 将 dynamic 参数设置为 false 会忽略新字段,如果 Elasticsearch 遇到未知字段,把 dynamic 设置为 strict 则会拒绝文档。
提示:使用 update mapping API 更新现有字段的 dynamic 设置。
你可以自定义 date detection 和 numeric detection 的动态字段映射规则。 要定义可应用于其他动态字段的自定义映射规则,请使用 dynamic_templates。
我们可以使用如下的例子来进行展示:
PUT test_index
"mappings":
"dynamic": "true",
"date_detection": false,
"numeric_detection": false,
"properties":
"name":
"properties":
"firstname":
"type": "text"
,
"lastname":
"type": "text"
PUT test_index/_doc/1
"null": null,
"bool": true,
"double": 1.0,
"long": 10,
"name":
"firstname": "xiaoguo",
"lastname": "liu"
,
"subjects": ["Math", "Chinese", "English"],
"date": "2015/09/02",
"numeric": "1.0"
GET test_index/_mapping
在上面,我们创建了一个叫做 test_index 的索引。我们把它的 dynamic 属性设置为 true。那么上面最后的一个命令返回的 mapping 值为:
"test_index" :
"mappings" :
"dynamic" : "true",
"date_detection" : false,
"numeric_detection" : false,
"properties" :
"bool" :
"type" : "boolean"
,
"date" :
"type" : "text",
"fields" :
"keyword" :
"type" : "keyword",
"ignore_above" : 256
,
"double" :
"type" : "float"
,
"long" :
"type" : "long"
,
"name" :
"properties" :
"firstname" :
"type" : "text"
,
"lastname" :
"type" : "text"
,
"numeric" :
"type" : "text",
"fields" :
"keyword" :
"type" : "keyword",
"ignore_above" : 256
,
"subjects" :
"type" : "text",
"fields" :
"keyword" :
"type" : "keyword",
"ignore_above" : 256
我们可以把 dynamic 设置为 runtime。运行如下的命令:
DELETE test_index
PUT test_index
"mappings":
"dynamic": "runtime",
"date_detection": false,
"numeric_detection": false,
"properties":
"name":
"properties":
"firstname":
"type": "text"
,
"lastname":
"type": "text"
PUT test_index/_doc/1
"null": null,
"bool": true,
"double": 1.0,
"long": 10,
"name":
"firstname": "xiaoguo",
"lastname": "liu"
,
"subjects": ["Math", "Chinese", "English"],
"date": "2015/09/02",
"numeric": "1.0"
GET test_index/_mapping
那么 test_index 的 mapping 为:
"test_index" :
"mappings" :
"dynamic" : "runtime",
"date_detection" : false,
"numeric_detection" : false,
"runtime" :
"bool" :
"type" : "boolean"
,
"date" :
"type" : "keyword"
,
"double" :
"type" : "double"
,
"long" :
"type" : "long"
,
"numeric" :
"type" : "keyword"
,
"subjects" :
"type" : "keyword"
,
"properties" :
"name" :
"properties" :
"firstname" :
"type" : "text"
,
"lastname" :
"type" : "text"
我们和上面的结果比较一下,还是可以找到它们直接的区别的。
Date detection
如果启用 date_detection(默认),则检查新字符串字段以查看其内容是否与 dynamic_date_formats 中指定的任何日期模式匹配。 如果找到匹配项,则会添加一个具有相应格式的新日期字段。
dynamic_date_formats 的默认值为:
[ "strict_date_optional_time","yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"
]
例如:
PUT my-index-000001/_doc/1
"create_date": "2015/09/02"
上面的命令生成一个叫做 my-index-000001 的索引。它的 mapping 是:
GET my-index-000001/_mapping
"my-index-000001" :
"mappings" :
"properties" :
"create_date" :
"type" : "date",
"format" : "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
在默认的情况下,date detection 是启动的,所以我们可以看到 create_date 被识别为 date 类型的数据。
禁止 date detection
在有些时候,我们可以禁止 date detection,这样摄入的数据可以当做是字符串。可以通过将 date_detection 设置为 false 来禁用动态日期检测:
DELETE my-index-000001
PUT my-index-000001
"mappings":
"date_detection": false
PUT my-index-000001/_doc/1
"create_date": "2015/09/02"
GET my-index-000001/_mapping
在上面,我们禁止了 date detection。上面最后一个命令返回的 mapping 值如下:
"my-index-000001" :
"mappings" :
"date_detection" : false,
"properties" :
"create_date" :
"type" : "text",
"fields" :
"keyword" :
"type" : "keyword",
"ignore_above" : 256
显然在这种情况下, create_date 就被当做是一般的 text 来进行处理。
定制 date detection 的日期格式
在上面,我们显示了在默认的情况下日期的检测格式如下:
[ "strict_date_optional_time","yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"
]
在实际的很多生产环境中,可能这种格式并不是我们想要的,比如对于中国的许多应用软件,它们的输出格式会和其它地区的格式有所不同。例如:
DELETE my-index-000001
PUT my-index-000001
"mappings":
"dynamic_date_formats": ["yyyy年MM月dd日"]
PUT my-index-000001/_doc/1
"create_date": "2022年03月18日"
GET my-index-000001/_mapping
在上面,我们重新定义了 date detection 的日期格式,那么上面最好一个命令的返回的值为:
"my-index-000001" :
"mappings" :
"dynamic_date_formats" : [
"yyyy年MM月dd日"
],
"properties" :
"create_date" :
"type" : "date",
"format" : "yyyy年MM月dd日"
从上面,我们可以看出来 create_date 是一个 date 类型的字段。
Numeric detection
虽然 JSON 支持本机浮点和整数数据类型,但某些应用程序或语言有时可能会将数字呈现为字符串。 通常正确的解决方案是显式映射这些字段,但可以启用 numeric detection(默认情况下禁用)以自动执行此操作:
DELETE my-index-000001
PUT my-index-000001
"mappings":
"numeric_detection": true
PUT my-index-000001/_doc/1
"my_float": "1.0",
"my_integer": "1"
GET my-index-000001/_mapping
在上面,我们启动了 number detection,那么最后一个命令返回的结果为:
"my-index-000001" :
"mappings" :
"numeric_detection" : true,
"properties" :
"my_float" :
"type" : "float"
,
"my_integer" :
"type" : "long"
更多阅读,请参阅文章 “Elasticsearch:Elasticsearch 中的数据强制匹配”。
以上是关于如何使用Elasticsearch实现对动态字段的搜索的主要内容,如果未能解决你的问题,请参考以下文章
如何通过Elasticsearch 6.x中的动态或未知字段进行聚合
Elasticsearch 8.X 如何动态的为正文添加摘要字段?
Elasticsearch 8.X 如何动态的为正文添加摘要字段?