Spring elastic : 嵌套字段的正确使用方法是什么?

Spring elastic : What is the correct way of using nested fields?

我对 ElasticSearch 还很陌生。我正在开展一个项目,我们需要在该项目中搜索包含一组两个对象 (OfferTranslation) 的对象 (Offer)。目标是基于一些 Offer 字段进行研究,但也有很多 OfferTranslation 字段。 这是 类 :

的缩小版

Offer.class (请注意,我用 @Field(type= FieldType.Nested) 对 Set 进行了注释,因此我可以进行嵌套查询,如官方文档中所述)

@org.springframework.data.elasticsearch.annotations.Document(indexName = "offer")
@DynamicMapping(DynamicMappingValue.False)
public class Offer implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @Field(type = FieldType.Long)
    private Long id;

    @OneToMany(mappedBy = "offer", targetEntity = OfferTranslation.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    @JsonIgnoreProperties(
        value = { "pictures", "videos", "owner", "contexts", "offer", "personOfInterests", "followers" },
        allowSetters = true
    )
    @Field(type = FieldType.Nested, store = true)
    private Set<OfferTranslation> offersTranslations = new HashSet<>();


}

OfferTranslation.class :

@DynamicMapping(DynamicMappingValue.False)
public class OfferTranslation implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @Field(type = FieldType.Long)
    private Long id;

    @NotNull
    @Size(max = 100)
    @Column(name = "title", length = 100, nullable = false)
    @Field(type = FieldType.Text)
    private String title;

    @NotNull
    @Size(max = 2000)
    @Column(name = "summary", length = 2000, nullable = false)
    @Field(type = FieldType.Text)
    private String summary;

    @Size(max = 2000)
    @Column(name = "competitor_context", length = 2000)
    @Field(type = FieldType.Text)
    private String competitorContext;

    @NotNull
    @Size(max = 2000)
    @Column(name = "description", length = 2000, nullable = false)
    @Field(type = FieldType.Text)
    private String description;

    @NotNull
    @Enumerated(EnumType.STRING)
    @Column(name = "maturity", nullable = false)
    @Field(type = FieldType.Auto)
    private RefMaturity maturity;

    @ManyToOne
    @Field(type = FieldType.Object, store = true)
    private RefLanguage language;

    @NotNull
    @Column(name = "created_at", nullable = false)
    @Field(type = FieldType.Date)
    private Instant createdAt;
}

预期的行为是我可以这样创建 nestedQueries :

QueryBuilder qBuilder = nestedQuery("offersTranslations",boolQuery().must(termQuery("offersTranslations.language.code",language)), ScoreMode.None);

但我得到的是一个异常:创建查询失败:路径 [offersTranslations] 下的 [nested] 嵌套对象不是嵌套类型

编辑:我可以使用普通查询访问 offersTranslations.language.code(目前这并没有真正打扰我)。但是我还是不太明白

我的映射显示字段 offersTranslations 不是您在上面看到的嵌套类型,但由于我使用了 @Field(type = FieldType.Nested) 我不太理解这种行为。 有人能解释一下吗?

{
  "offer" : {
    "mappings" : {
      "properties" : {
        "_class" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "categories" : {
          "properties" : {
            "id" : {
              "type" : "long"
            }
          }
        },
        "criteria" : {
          "properties" : {
            "id" : {
              "type" : "long"
            }
          }
        },
        "id" : {
          "type" : "long"
        },
        "keywords" : {
          "properties" : {
            "id" : {
              "type" : "long"
            }
          }
        },
        "offersTranslations" : {
          "properties" : {
            "competitorContext" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "createdAt" : {
              "type" : "date"
            },
            "description" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "id" : {
              "type" : "long"
            },
            "language" : {
              "properties" : {
                "code" : {
                  "type" : "text",
                  "fields" : {
                    "keyword" : {
                      "type" : "keyword",
                      "ignore_above" : 256
                    }
                  }
                },
                "id" : {
                  "type" : "long"
                },
                "name" : {
                  "type" : "text",
                  "fields" : {
                    "keyword" : {
                      "type" : "keyword",
                      "ignore_above" : 256
                    }
                  }
                }
              }
            },
            "maturity" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "state" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "summary" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "title" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "updatedAt" : {
              "type" : "date"
            }
          }
        },
        "units" : {
          "properties" : {
            "id" : {
              "type" : "long"
            }
          }
        }
      }
    }
  }
}

索引映射是如何创建的?它看起来不像 Spring Data Elasticsearch 写了这个映射。如您所见,缺少 offerTranslations 的嵌套类型,文本字段有一个 .keyword 子字段。这看起来数据被插入到索引中而没有定义映射,因此 Elasticsearch 进行了自动映射。在这种情况下,不使用 @Field 注释的值。

您需要让 Spring Data Elasticsearch 使用映射创建索引。如果索引不存在并且您正在使用 Spring Data Elasticsearch 存储库,或者您需要在应用程序中使用 IndexOperations.createWithMapping 函数,这将自动发生。

我注意到的另一件事:您似乎对不同的 Spring 数据存储使用了相同的实体 class,严重混合了注释。您应该为不同的商店使用不同的实体。

解决步骤:

  • 使用 Kibana 确保删除 /_mapping
  • 在您的实体 类 中查找您需要的可能位于 @JsonIgnoreProperties
  • 中的对象
  • 确保你很乐意加载你的 toMany 关系的属性(否则 elastic 不会为你从未提供的数据创建映射)
  • 根据这个小经验,我会说避免使用嵌套字段,我看不出使用它们有任何好处。因此,请检查您是否也属于这种情况!