【问题标题】:spring-data-elasticsearch using Criteria with nested Objectsspring-data-elasticsearch 使用带有嵌套对象的条件
【发布时间】:2014-03-13 18:48:58
【问题描述】:

我对 elasticsearch 和 spring-data-elasticsearch 还是很陌生,在查询嵌套对象时遇到了一些问题。

我使用 ElasticSearch 存储库将嵌套模型实例保存在 elasticsearch 中。结果,elasticsearch 中只有一个条目包含所有数据,据我了解,这意味着我有一个嵌套文档。

我需要使用 Criteria 实现一个相对复杂的查询来迭代地构建查询。 当我尝试使用点符号访问嵌套属性时,例如

startPoint.providingTimeRange.startTime

我没有得到任何搜索结果(但在 elasticsearch 中存在匹配的数据)。

Spring Data Elastic Search with Nested Fields and mapping 中,我发现spring-data-elasticsearch 可以使用nestedQuery 查询嵌套对象。

有没有办法将 Criteria 搜索与nestedQuery 结合起来?

提前谢谢你, 克里斯托夫

更新 1: 为了提供一些示例代码,我创建了一个演示项目,其中包含与我的真实项目中类似的嵌套对象:https://github.com/empulse-gmbh/elasticsearchtest

该示例将要查找特定时间、特定地点的 FoodTrucks。

在这个例子中,我使用了一个存储库来保存我的嵌套实体。

我使用了两种(都不起作用)方法来查询嵌套对象:

        /*
         * add search criteria by timerange. it is assumed in timerange from
         * is always before to.
         */
        TimeRange searchTimeRange = foodTruckSearch.getSearchTimeRange();
        if (searchTimeRange != null) {

            String startTimePath = "locationPoint.timeRange.from";
            String endTimePath = "locationPoint.timeRange.to";

            searchCriteria = searchCriteria.and(
                    new Criteria(startTimePath).between(searchTimeRange
                            .getFrom().getTime(), searchTimeRange.getTo()
                            .getTime())).or(
                    new Criteria(endTimePath).between(searchTimeRange
                            .getFrom().getTime(), searchTimeRange.getTo()
                            .getTime()));
        }

和:

    TimeRange searchTimeRange = foodTruckSearch.getSearchTimeRange();
    if (searchTimeRange != null) {

        String startTimePath = "locationPoint.timeRange.from";
        String endTimePath = "locationPoint.timeRange.to";

        searchQuery.must(nestedQuery(
                "locationPoint",
                boolQuery().should(
                        rangeQuery(startTimePath).from(
                                searchTimeRange.getFrom().getTime()).to(
                                searchTimeRange.getTo().getTime())).should(
                        rangeQuery(endTimePath).from(
                                searchTimeRange.getFrom()).to(
                                searchTimeRange.getTo()))));

    }

更新 2: 感谢 Mohsin Husen,我能够使用嵌套搜索。按时间范围搜索现在有效。 除了 Mohsin 的建议之外,我还必须更改创建和启动本地 Elasticsearch 实例的方式:

elasticSearchNode = NodeBuilder.nodeBuilder().clusterName("foodtruck-test").local(true).build();

没有给出clusterName的嵌套文档不会被创建。

我的下一个(希望是最后一个)问题是使用 geo_distance 搜索过滤器。如果我在 Elasticsearch 中做对了,我会使用模糊搜索查询,例如“like”或“range”,并使用过滤器过滤是/否或距离过滤器。

所以我尝试将 QueryBuilder 和 FilterBuilder 与 spring-data-elasticsearch 一起使用,但失败了。

根据文档中的示例,我重构了我的模型,因此 Location 类使用 spring-data-elasticsearch 对象 GeoPoint。

我用这个来搜索:

    /*
     * add search criteria for radius search
     */
    FilterBuilder searchFilter = null;
    if (foodTruckSearch.getLatitude() != null
            && foodTruckSearch.getLongitude() != null) {

        if (foodTruckSearch.getSearchRadiusInKilometers() == null) {
            foodTruckSearch.setSearchRadiusInKilometers(5);
        }

        searchFilter = geoDistanceFilter("location.point")
                .distance(
                        foodTruckSearch.getSearchRadiusInKilometers()
                                + "km").lat(foodTruckSearch.getLatitude())
                .lon(foodTruckSearch.getLongitude());

    }

    if (searchFilter != null) {
        NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(
                searchQuery, searchFilter);
        return IteratorUtils.toList(foodTruckRepository.search(
                nativeSearchQuery).iterator());
    } else {
        return IteratorUtils.toList(foodTruckRepository.search(searchQuery)
                .iterator());
    }

我得到的例外是:

org.elasticsearch.action.search.SearchPhaseExecutionException: Failedtoexecutephase[query_fetch],
allshardsfailed;shardFailures{
    [1][searchexample][0]: SearchParseException[[searchexample][0]: query[MatchNoDocsQuery],
    from[0],
    size[10]: ParseFailure[Failedtoparsesource[{
    "from": 0,
    "size": 10,
    "query": {
        "bool": {

        }
    },
    "post_filter": {
        "geo_distance": {
            "location.point": [6.9599115,
            50.9406645],
            "distance": "10km"
        }
    }
}]]];nested: QueryParsingException[[searchexample]failedtofindgeo_pointfield[location.point]]   ;
}

是否有任何示例可用于如何将 geo_distance(嵌套?)过滤器与 spring-data-elasticsearch 一起使用?

【问题讨论】:

  • 请分享一些代码,以便我们帮助解决问题。即实体和搜索请求等。
  • @MohsinHusen 请看我的Update1,一个完整的demo项目现已发布

标签: java spring nested elasticsearch spring-data


【解决方案1】:

异常是由于在

中提供了默认映射器引起的
return new ElasticsearchTemplate(elasticSearchNode.client());

删除这个错误的映射器会导致 spring-data-elasticsearch 将正确的映射放入 Elasticsearch。

如果您想为 geo_distance 搜索启用实体,请使用

@GeoPointField

用于标记包含纬度/经度值的字段的注释。

此外,FoodTruckServiceImpl 已更改为使用过滤器,而不是查询。

GitHub 中的项目已更新,是一个功能齐全的示例。在 spring-data-elasticsearch 的第一步中,请随意使用 FoodTruck 示例。

【讨论】:

    【解决方案2】:

    在没有详细阅读的情况下,我发现了您代码中的一些问题,如下所示

    1) 当您使用多级嵌套文档时,您必须使用快照版本(您正在使用),因为 spring data elasticsearch M1 版本具有已修复的错误。 (https://jira.springsource.org/browse/DATAES-53)

    2) 您在实体中声明嵌套类型错误,必须如下

        @Field(type=FieldType.Nested)
        private LocationPoint locationPoint;
    
        @Field(type = FieldType.Nested)
        private TimeRange timeRange;
    

    3) 标准查询不会处理嵌套对象,它是为处理简单实体而构建的,因为它使用 query_string 来查询数据。因此使用 elasticsearchTemplate 或存储库来查询嵌套对象。

    4) 您必须为嵌套对象指定完整路径

    例如,在搜索 locationPoint.timeRange.from 和 locationPoint.timeRange.to 时,您必须按如下方式使用

     String startTimePath = "locationPoint.timeRange.from";
        String endTimePath = "locationPoint.timeRange.to";
    
        searchQuery.must(nestedQuery(
                "locationPoint.timeRange",
                boolQuery().should(
                        rangeQuery(startTimePath).from(
                                searchTimeRange.getFrom().getTime()).to(
                                searchTimeRange.getTo().getTime())).should(
                        rangeQuery(endTimePath).from(
                                searchTimeRange.getFrom()).to(
                                searchTimeRange.getTo()))));
    

    希望这能解决您的问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-07
      • 1970-01-01
      • 2016-03-28
      • 1970-01-01
      • 2018-03-10
      • 1970-01-01
      • 2016-01-02
      相关资源
      最近更新 更多