Spring数据MongoDb根据嵌套数组字段的最后一个元素查询

Spring data MongoDb query based on last element of nested array field

我有以下数据(汽车):

[
    {
        "make" : “Ferrari”,
        "model" : “F40",
        "services" : [ 
            {
                "type" : "FULL",
                “date_time" : ISODate("2019-10-31T09:00:00.000Z"),
            }, 
            {
                "type" : "FULL",
                "scheduled_date_time" : ISODate("2019-11-04T09:00:00.000Z"),
            }
        ],
    },
    {
        "make" : "BMW",
        "model" : “M3",
        "services" : [ 
            {
                "type" : "FULL",            
                "scheduled_date_time" : ISODate("2019-10-31T09:00:00.000Z"),
            }, 
            {
                "type" : "FULL",             
                “scheduled_date_time" : ISODate("2019-11-04T09:00:00.000Z"),
            }
        ],
    }
]

使用 Spring 数据 MongoDb 我想要一个查询来检索所有 Cars 其中 scheduled_date_time[ services 数组中 last 项的 =35=] 处于特定日期范围内。

我之前在使用服务数组中的第一项时使用的查询如下:

mongoTemplate.find(Query.query(
        where("services.0.scheduled_date_time").gte(fromDate)
            .andOperator(
                where("services.0.scheduled_date_time").lt(toDate))),
        Car.class);

请注意 0 索引,因为它是第一个而不是最后一个(根据我当前的要求)。

我认为将聚合与投影一起使用 .arrayElementAt(-1) 可以解决问题,但我还没有完全做到这一点。我目前的努力是:

Aggregation agg = newAggregation(
        project().and("services").arrayElementAt(-1).as("currentService"),
        match(where("currentService.scheduled_date_time").gte(fromDate)
            .andOperator(where("currentService.scheduled_date_time").lt(toDate)))
    );

    AggregationResults<Car> results = mongoTemplate.aggregate(agg, Car.class, Car.class);

    return results.getMappedResults();

感谢任何帮助建议。

谢谢,

此 mongo 聚合检索服务数组中最后一项的 scheduled_date_time 在特定日期范围内的所有汽车。

 [{
        $addFields: {
            last: {
                $arrayElemAt: [
                    '$services',
                    -1
                ]
            }
        }
    }, {
        $match: {
            'last.scheduled_date_time': {
                $gte: ISODate('2019-10-26T04:06:27.307Z'),
                $lt: ISODate('2019-12-15T04:06:27.319Z')
            }
        }
    }]

我试图在 spring-data-mongodb 中写入它,但不幸的是。

他们还不支持 $addFields,请参阅 here

自版本 2.2.0 RELEASE spring-data-mongodb 包含 Aggregation Repository Methods

上面的查询应该是

interface CarRepository extends MongoRepository<Car, String> {

    @Aggregation(pipeline = {
            "{ $addFields : { last:{ $arrayElemAt: [$services,-1] }} }",
            "{ $match: { 'last.scheduled_date_time' : { $gte : '$?0',  $lt: '$?1' } } }"
    })
    List<Car> getCarsWithLastServiceDateBetween(LocalDateTime start, LocalDateTime end);
}

此方法记录此查询

[{ "$addFields" : { "last" : { "$arrayElemAt" : ["$services", -1]}}}, { "$match" : { "last.scheduled_date_time" : { "$gte" : "19-11-03T03:00:00Z", "$lt" : "19-11-05T03:00:00Z"}}}]

日期参数解析不正确。我没有花太多时间让它发挥作用。

如果您想要汽车 ID,这可能有用。

public List<String> getCarsIdWithServicesDateBetween(LocalDateTime start, LocalDateTime end) {

        return template.aggregate(newAggregation(
                unwind("services"),
                group("id").last("services.date").as("date"),
                match(where("date").gte(start).lt(end))
        ), Car.class, Car.class)
                .getMappedResults().stream()
                .map(Car::getId)
                .collect(Collectors.toList());

    }

查询日志

[{ "$unwind" : "$services"}, { "$group" : { "_id" : "$_id", "date" : { "$last" : "$services.scheduled_date_time"}}}, { "$match" : { "date" : { "$gte" : { "$date" : 1572750000000}, "$lt" : { "$date" : 1572922800000}}}}]