Поддерживает ли Spring Data MongoDb оператор агрегации массива $filter?

Я пытаюсь реализовать в Spring Data с помощью MongoTemplate следующий рабочий запрос mongoDb:

db.answers.aggregate([
        { "$match" : { "object_id" : "1" } },
        { "$project": { 'answer_list': 1, 'profile': { $filter : { input: '$answer_list', as: 'answer', cond: { $eq: [ '$$answer.question', 2 ] } } } } },
        { "$unwind" : "$profile"},
        { "$unwind" : "$answer_list"},
        { "$group" : { "_id" : { "question" : "$answer_list.question", "answer" : "$answer_list.answer", "criteria" : "$profile.answer"}, "count" : { "$sum" : 1 } } },
        { "$sort" : { "_id.question" : 1, "_id.answer" : 1 } }
]);

Коллекция имеет такую ​​структуру:

{
"_id" : ObjectId("..."),
"object_id" : ObjectId("..."),
"answer_list" : [ 
    {
        "question" : NumberLong(0),
        "answer" : NumberLong(0)
    }, 
    {
        "question" : NumberLong(1),
        "answer" : NumberLong(2)
    }, 
    {
        "question" : NumberLong(2),
        "answer" : NumberLong(2)
    }
]}

То, что я пытаюсь сделать здесь, это отчет о данных отправки простого опроса. Вопрос заключается в следующем: «Как пользователи, ответившие 0 на первый вопрос, ответили на второй вопрос?» Я провел весь день в поисках документов SpringData Mongo Db, но ничего не нашел. Кто-нибудь может помочь?

ТИА


person optimalab    schedule 12.09.2016    source источник
comment
Я создал DATAMONGO-1491, чтобы добавить поддержку $filter.   -  person Christoph Strobl    schedule 13.09.2016
comment
Спасибо Кристоф. Я надеюсь найти обходной путь, пока он не будет реализован   -  person optimalab    schedule 13.09.2016


Ответы (2)


Вы можете обойти эту проблему, предоставив свой собственный файл AggregationExpression.

ProjectionOperation agg = Aggregation.project() //

      .and(new AggregationExpression() {

        @Override
        public DBObject toDbObject(AggregationOperationContext context) {

          DBObject filterExpression = new BasicDBObject();
          filterExpression.put("input", "$answer_list");
          filterExpression.put("as", "answer");
          filterExpression.put("cond", new BasicDBObject("$eq2", Arrays.<Object> asList("$$answer.question", 2)));

          return new BasicDBObject("$filter", filterExpression);
        }
      }).as("profile");
person Christoph Strobl    schedule 13.09.2016
comment
Это выглядит именно то, что мне нужно. Единственная проблема в том, что интерфейс AggregationExpression не является общедоступным, если я что-то не упускаю... - person optimalab; 13.09.2016
comment
Я создал jira.spring.io/browse/DATAMONGO-1492, предполагая, что интерфейс должен быть общедоступным. Я все еще ищу способ реализовать ваше предложение... - person optimalab; 13.09.2016
comment
дох... мой плохой - это должно быть общедоступным - спасибо за регистрацию проблемы! Поскольку AggregationExpression является частным пакетом, вы можете добавить пакет org.springframework.data.mongodb.core.aggregation в свой проект и создать общедоступный класс/интерфейс, FilterExpression реализующий/расширяющий AggregationExpression внутри этого пакета. Тогда просто используйте тот оттуда. - person Christoph Strobl; 13.09.2016
comment
Я приближаюсь... это моя реализация: public class FilterExpression implements AggregationExpression { // private fields and constructor omitted for brevity @Override public DBObject toDbObject( AggregationOperationContext context ) { DBObject filterExpression = new BasicDBObject(); filterExpression.put("input", input); filterExpression.put("as", as); filterExpression.put("cond", cond); return new BasicDBObject("$filter", filterExpression); } } - person optimalab; 13.09.2016
comment
Выражение кажется правильным (вызов toDBObject() и toJson() дает: {$filter: {input: $answer_list, as: answer, cond: {$eq2: [$$answer.question, 2] } } }) но я получаю это исключение: org.springframework.data.mapping.PropertyReferenceException: No property answer found for type Opinion! at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:77) ... - person optimalab; 13.09.2016
comment
Я получаю то же исключение с этим кодом: ` проект (список_ответов). ) ),` - person optimalab; 13.09.2016
comment
есть немного кода, который я могу запустить/отладить? Может быть, суть с неудачным тесткейсом? - person Christoph Strobl; 13.09.2016
comment
Я создал репозиторий GitHub: github.com/optimalab/filterExpressionTest Если я все сделал правильно вы должны просто запустить mvn test - person optimalab; 13.09.2016
comment
Я играю с демонстрационным проектом... оставив только проект. Я успешно выполнил запрос, удалив answer_list в вызове проекта. Может быть, это проблема, связанная с двойной ссылкой на одно и то же поле? - person optimalab; 13.09.2016
comment
Здравствуйте, спасибо, что поделились кодом. Попробуйте выполнить следующие действия для проекта project().andInclude(Fields.from(Fields.field("answer_list", "answerList"))).and(expression).as("profile"). Тем не менее, агрегация не возвращает никакого результата, но это, кажется, другая проблема, поскольку я пытался запустить отрезанный комментарий против тестовых данных на консоли, которые также ничего не вернули. - person Christoph Strobl; 14.09.2016
comment
В сгенерированном запросе есть проблема: псевдоним для addInclude неверен. { "$project" : { "answer_list" : "$answer_list" должно быть { "$project" : { "answerList" : "$answer_list" - person optimalab; 14.09.2016
comment
Причиной может быть вызов Fields.field("answer_list", "answerList"). В документации сказано, что первый параметр — это имя (псевдоним), второй — цель. Но если я инвертирую параметры, я снова получаю PropertyReferenceError - person optimalab; 14.09.2016
comment
Копаясь в коде, я обнаружил что-то странное в org.springframework.data.mapping.PropertyPath, строка 259. На символе «_» есть разделитель для просмотра существующих свойств, поэтому answer_list становится [answer, list], а затем он терпит неудачу, потому что поле ответа не найдено. Зачем этот разветвитель? - person optimalab; 14.09.2016
comment
Хорошо, я наконец решил проблему: ваше последнее предложение было правильным. Fields.field(answer_list, answerList) правильный, потому что целью должно быть сопоставленное имя поля (answerList). Может быть, это должно быть выделено в документах? Так что мне пришлось исправить ссылки в разделе группы. Я обновил репозиторий git для будущих ссылок. Спасибо за вашу драгоценную помощь!! - person optimalab; 14.09.2016
comment
Разрешив DATAMONGO-1492, вы сможете напрямую использовать AggregationExpression public сейчас . Изменение коснулось: 1.8.5 (Gosling SR5), 1.9.3 (Hopper SR3) и 1.10 RC1 (Ingalls). - person Christoph Strobl; 22.09.2016

Есть еще одна альтернатива с использованием Aggregation

Aggregation aggregation = newAggregation(
                           project()
                           .and(filter("answer_list")
                           .as("answer")
                       .by(valueOf("answer.question").equalToValue(2)))
                           .as("profile"));
AggregationResults<OutputType> profile = mongoTemplate.aggregate(aggregation, InputType.class, OutputType.class);

Возможно, я не смогу правильно ответить на ваш вопрос, но я просто хотел дать другой подход к агрегации, поскольку существует меньшее количество примеров с использованием Aggregation.

В project() вы можете указать ключи, которые вы хотите в своем ответе, так как это метод varargs

person Anshul    schedule 22.06.2020