一、背景
记一次在ES中属性字段使用object做类型时,关联关系丢失的情况;比如(a,1)、(b,2)、(c,3),进行数据查询的时候保证不了只有关系对应为(a,1)||(b,2)||(c,3)时能够查询出数据,关系为(a,2)||(c,1)等错误关系时也可以查询出数据。
二、原因分析
在网上查询了一下原因,原来是Elasticsearch(lucene)使用的库没有内部对象的概念,即内部对象会被扁平化为一个简单的字段名称和值列表。如背景中的例子在文档内部中的存储如下
name | [a, b, c] |
---|---|
value | [1, 2, 3] |
三、nested的作用
nested类型是对象数据类型的专用版本,它允许对象数组以可以彼此独立查询的方式进行索引。使用nested类型后背景中的例子在文档内部的存储方式如下
name | a | b | c |
---|---|---|---|
value | 1 | 2 | 3 |
四、测试用例
1、测试数据准备
(1)创建index、type
PUT test_xz
{
"mappings": {
"word": {
"properties": {
"attribute_standard": {
"type":"nested",
"properties": {
"attr_name":{
"type":"keyword"
},
"attr_value":{
"type":"keyword"
}
}
}
}
}
}
}
(2)插入测试数据
PUT /test_xz/word/1
{
"attribute_standard": [
{
"attr_name": "测试数据",
"attr_value": [
"40",
"20"
]
}
]
}
PUT /test_xz/word/2
{
"attribute_standard": [
{
"attr_name": "join",
"attr_value": [
"20",
"30",
"40"
]
}
]
}
PUT /test_xz/word/3
{
"attribute_standard": [
{
"attr_name": "able",
"attr_value": [
"20"
]
},
{
"attr_name": "baoshi",
"attr_value": [
"14"
]
},
{
"attr_name": "condi",
"attr_value": [
"30"
]
},
{
"attr_name": "xiye",
"attr_value": [
"50"
]
}
]
}
2、单个属性查询
GET /test_xz/word/_search?pretty
{
"query": {
"bool": {
"filter": [
{
"nested": {
"path": "attribute_standard",
"query": {
"bool": {
"must": [
{
"term": {
"attribute_standard.attr_name": "join"
}
},
{
"terms": {
"attribute_standard.attr_value": ["20","10"]
}
}
]
}
}
}
}
]
}
}
}
结果:
3、多个属性查询
GET /test_xz/word/_search?pretty
{
"query": {
"bool": {
"filter": [
{
"nested": {
"path": "attribute_standard",
"query": {
"bool": {
"must": [
{
"term": {
"attribute_standard.attr_name": "able"
}
},
{
"terms": {
"attribute_standard.attr_value": ["20"]
}
}
]
}
}
}
},
{
"nested": {
"path": "attribute_standard",
"query": {
"bool": {
"must": [
{
"term": {
"attribute_standard.attr_name": "baoshi"
}
},
{
"terms": {
"attribute_standard.attr_value": ["14"]
}
}
]
}
}
}
}
]
}
}
}
结果:
4、JavaAPI应用
Attribute a1 = new Attribute().setAttrName("able").setAttrValue("20");
Attribute a2 = new Attribute().setAttrName("baoshi").setAttrValue("14");
List<Attribute> attributeList = Lists.newArrayList(a1, a2);
for (Attribute attribute : attributeList) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//attrValue多个时以半角分号分隔
List<String> attrValues = Arrays.stream(attribute.getAttrValue().split(";"))
.filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList());
boolQueryBuilder = boolQueryBuilder
.must(QueryBuilders.termQuery("attribute_standard.attr_name", attribute.getAttrName()))
.must(QueryBuilders.termsQuery("attribute_standard.attr_value", attrValues));
queryBuilder.filter(QueryBuilders.nestedQuery("attribute_standard", boolQueryBuilder, ScoreMode.None));
}
参考:https://blog.youkuaiyun.com/laoyang360/article/details/82950393