Вернуть совпадающие элементы массива с Morphia - PullRequest
0 голосов
/ 29 мая 2018

Я ищу способ отфильтровать все объекты из данного List of Object после предоставления некоторых conditions.

Например,

Класс A

@Entity(value = "tbl_A")
public class A {

private String notes;
@Embedded
private List<SampleObject> sampleObject;

....getter and setter ...
}

Класс B

@Embedded
public class SampleObject {
    private boolean read;
    private boolean sentByBot;

   ... getter and setter ...
   }

Теперь я хочу собрать только SampleObject, для которого параметр sentByBot имеет значение true.Я использую следующий подход:

Query<A> queryForA = datastore.find(A.class);
queryForA.field("sampleObject.sentByBot").equal(false).retrievedFields(true, "sampleObject.sentByBot");

Выше код дает мне полный список objects, которые имеют sampleObject.sentByBot true и false оба.

Я также попробовал filter подход, т.е.

 queryForA.filter("sampleObject.sentByBot", false).retrievedFields(true, "sampleObject.sentByBot");

Но не повезло.Есть ли способ получить только те поля, для которых sampleObject.sentByBot установлено в true?

Edit

После реализации следующего кода я получил это:

Database Image

enter image description here

Код

  AggregationOptions options = AggregationOptions.builder()
              .outputMode(AggregationOptions.OutputMode.CURSOR)
              .build();

  //System.out.println(options.toString());

  Projection filterProjection = Projection.projection(
              "sampleObjects",
              Projection.expression(
                      "$filter",
                      new BasicDBObject("input","$sampleObjects")
                              .append("cond",new BasicDBObject("$eq", Arrays.asList("$$this.sentByBot",true)))
              )
      );

 AggregationPipeline pipeline = datastore.createAggregation(A.class)
              .match(datastore.createQuery(A.class).filter("sampleObjects.sentByBot", true))
              .project(
                      Projection.projection("fieldA"),
                      Projection.projection("fieldB"),
                      filterProjection
              );

      Iterator<A> cursor = pipeline.aggregate(A.class, options);

Выход

"Command failed with error 28646: '$filter only supports an object as its argument'. The full response is { \"ok\" : 0.0, \"errmsg\" : \"$filter only supports an object as its argument\", \"code\" : 28646 }"

1 Ответ

0 голосов
/ 29 мая 2018

Как указывалось ранее, для возврата «нескольких» элементов массива, соответствующих заданному условию, требуется оператор конвейера агрегации $filter в проекции.Чтобы выдать такой оператор агрегации с Morphia, вам нужно что-то вроде этого:

Projection filterProjection = projection(
        "sampleObjects",
        expression(
                "$filter",
                new BasicDBObject("input","$sampleObjects")
                .append("cond",new BasicDBObject("$eq", Arrays.asList("$$this.sentByBot",true)))
        )
);

AggregationPipeline pipeline = datastore.createAggregation(A.class)
        .match(datastore.createQuery(A.class).filter("sampleObjects.sentByBot", true))
        .project(
                projection("fieldA"),
                projection("fieldB"),
                filterProjection
        );

, который выдает конвейер на сервер как:

[ 
  { "$match" : { "sampleObjects.sentByBot" : true } },
  { "$project" : {
    "fieldA" : 1,
    "fieldB" : 1,
    "sampleObjects" : { 
      "$filter" : {
        "input" : "$sampleObjects",
        "cond" : { "$eq" : [ "$$this.sentByBot", true ] }
      }
    }
  }}
]

и возвращает только элементы массива изсоответствующие документы, которые также соответствуют условию:

{ 
  "className" : "com.snakier.example.A" ,
  "_id" : { "$oid" : "5b0ce52c6a6bfa50084c53aa"} ,
  "fieldA" : "something" ,
  "fieldB" : "else" ,
  "sampleObjects" : [
   { "name" : "one" , "read" : false , "sentByBot" : true} ,
   { "name" : "three" , "read" : true , "sentByBot" : true}
  ]
}

Обратите внимание, что вам нужно вручную построить аргумент expression() из DBObject(), так как в Morphia не поддерживаются текущие «строители» для этого типа операции,Ожидается, что будущие выпуски изменится на интерфейс Document, который уже некоторое время является стандартным в базовом драйвере Java.

В качестве полного примера списка:

package com.snakier.example;

import com.mongodb.AggregationOptions;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import org.bson.types.ObjectId;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
import org.mongodb.morphia.aggregation.AggregationPipeline;
import org.mongodb.morphia.aggregation.Projection;
import org.mongodb.morphia.annotations.Embedded;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import static org.mongodb.morphia.aggregation.Projection.*;

public class Application {

    public static void main(String[] args) {
        final Morphia morphia = new Morphia();

        morphia.mapPackage("com.snakier.example");

        final Datastore datastore = morphia.createDatastore(new MongoClient(),"example");

        // Clean example database
        datastore.getDB().getCollection("example").drop();

        // Create some data
        final A first = new A("something","else");
        final A second = new A("another","thing");

        final SampleObject firstSample = new SampleObject("one", false, true);
        final SampleObject secondSample = new SampleObject("two", false, false);
        final SampleObject thirdSample = new SampleObject("three", true,true);
        final SampleObject fourthSample = new SampleObject("four", true, false);

        first.setSampleObjects(Arrays.asList(firstSample,secondSample,thirdSample));
        datastore.save(first);

        second.setSampleObjects(Arrays.asList(fourthSample));
        datastore.save(second);

        AggregationOptions options = AggregationOptions.builder()
                .outputMode(AggregationOptions.OutputMode.CURSOR)
                .build();

        //System.out.println(options.toString());

        Projection filterProjection = projection(
                "sampleObjects",
                expression(
                        "$filter",
                        new BasicDBObject("input","$sampleObjects")
                        .append("cond",new BasicDBObject("$eq", Arrays.asList("$$this.sentByBot",true)))
                )
        );

        AggregationPipeline pipeline = datastore.createAggregation(A.class)
                .match(datastore.createQuery(A.class).filter("sampleObjects.sentByBot", true))
                .project(
                        projection("fieldA"),
                        projection("fieldB"),
                        filterProjection
                );

        Iterator<A> cursor = pipeline.aggregate(A.class, options);

        while (cursor.hasNext()) {
            System.out.println(morphia.toDBObject(cursor.next()));
        }

    }
}

@Entity(value = "example")
class A {
    @Id
    private ObjectId id;
    private String fieldA;
    private String fieldB;

    @Embedded
    private List<SampleObject> sampleObjects;

    public  A() {

    }

    public A(String fieldA, String fieldB) {
        this.fieldA = fieldA;
        this.fieldB = fieldB;
    }

    public void setSampleObjects(List<SampleObject> sampleObjects) {
        this.sampleObjects = sampleObjects;
    }

    public List<SampleObject> getSampleObjects() {
        return sampleObjects;
    }

    public ObjectId getId() {
        return id;
    }

    public String getFieldA() {
        return fieldA;
    }

    public void setFieldA(String fieldA) {
        this.fieldA = fieldA;
    }

    public void setFieldB(String fieldB) {
        this.fieldB = fieldB;
    }

    public String getFieldB() {
        return fieldB;
    }

}

@Embedded
class SampleObject {
    private String name;
    private boolean read;
    private boolean sentByBot;

    public SampleObject() {

    }

    public SampleObject(String name, boolean read, boolean sentByBot) {
        this.name = name;
        this.read = read;
        this.sentByBot = sentByBot;
    }

    public String getName() {
        return name;
    }

    public boolean isRead() {
        return read;
    }

    public boolean isSentByBot() {
        return sentByBot;
    }
}
...