Spring данные MongoDb запрос на основе последнего элемента поля вложенного массива - PullRequest
1 голос
/ 26 октября 2019

У меня есть следующие данные ( Автомобили ):

[
    {
        "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 Я бы хотел запросить, чтобы получить все Автомобили , где schedule_date_time элемента last в массиве services находится между определенным диапазоном дат.

Запрос, который я использовал ранее при использованииПервый элемент в массиве служб выглядит следующим образом:

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();

Любые предложения помощи приветствуются.

Спасибо,

1 Ответ

2 голосов
/ 26 октября 2019

Эта агрегация монго извлекает все автомобили, на которых значение schedule_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, см. здесь .

Начиная с версии 2.2.0 RELEASE spring-data-mongodb включает в себя Методы репозитория агрегации

Вышеприведенный запрос должен быть

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" : "$2019-11-03T03:00:00Z", "$lt" : "$2019-11-05T03:00:00Z"}}}]

Параметры даты обрабатываются неправильно. Я не тратил много времени, чтобы заставить его работать.

Если вы хотите, чтобы идентификаторы автомобилей работали.

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}}}}]
...