首页 > 解决方案 > 使用 Morphia 返回匹配的数组元素

问题描述

我正在寻找一种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真假。

我也尝试过filter方法,即

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

但没有运气。有没有办法只获得那些sampleObject.sentByBot设置为真的字段?

编辑

实现以下代码后,我得到了这个:

数据库映像

在此处输入图像描述

代码

  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 }"

标签: javamongodbmongodb-queryaggregation-frameworkmorphia

解决方案


如前所述,为了只返回与给定条件匹配的“多个”数组元素,您想要的是$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()手动构建 in 参数,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;
    }
}

推荐阅读