$ elemMatch使用Java对嵌套数组进行过滤

时间:2020-03-18 16:45:38

标签: java mongodb

我有JSON

{
  "responseCode": 200,
  "productList": {
    "count": 25,
    "products": [
      {
        "id": 403492,
        "description": null,
        "plans": [
          {
            "name": "test1",
            "type": "G"
          },
          {
            "name": "test2",
            "type": "Y"
          }
        ]
      }
    ],
  }
}

我只想为“ G”类型获得计划,我不想将test2返回json 我尝试使用$ elemMatch,但无法正常工作

Document query = new Document();

query.append("plans", new Document().append("$elemMatch", value));

2 个答案:

答案 0 :(得分:0)

很明显, $elemMatch(query) 是一种查询方法,用于在数组元素与查询匹配时获取数据!

您想要的是 $elemMatch(projection) !但是根据official document,此预测存在问题:

$ 运算符和 $ elemMatch 运算符都投影第一个 根据条件匹配数组中的元素。

这意味着您将只获得第一个与query匹配的元素, 所以可能不符合您的需求!

有2种方法:

1-在应用程序端投影所需的数据,也许使用java流api!

2-使用 $filter(aggregation)

第二种方式(java代码):

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import org.bson.conversions.Bson;

import java.util.Arrays;

import static com.mongodb.client.model.Aggregates.match;
import static com.mongodb.client.model.Aggregates.project;
import static com.mongodb.client.model.Filters.*;

public class Test {

public static void main(String[] args) throws Exception
{
    MongoClient client = MongoClients.create(
            "mongodb://localhost:27017"
    );

    MongoDatabase database = client.getDatabase("TEST");
    MongoCollection<Document> documentMongoCollection =
            database.getCollection("test");

    Document document = documentMongoCollection.aggregate(
            Arrays.asList(
                    match(elemMatch("productionList.products.plans" , eq("type" ,"G")))

                    ,project(new Document("productionList.products" ,
                            map("productionList.products" , "this" ,
                                    new Document("plans" ,
                                            filter("$this.plans" ,
                                                    "plan" ,
                                                    new Document(
                                                            "$eq" ,
                                                            Arrays.asList("$$plan.type" , "G")
                                                    )))
                                            .append("description" , "$$this.description")//make it present
                                            .append("id" , "$$this.id"))) //make it present
                            .append("responseCode" , 1) //include
                            .append("productionList.count" , 1) //include
            )
    )).first();



    System.out.println(document.toJson());
}


private final static Bson filter(String arrayName , String as , Bson cond)
{
    //so to do it !

    Document document = new Document("input" , "$"+arrayName);

    if(as!=null && !as.isEmpty())
        document.append("as" , as);

    document.append("cond" , cond);

    return new Document("$filter" , document);
}

private final static Bson map(String arrayName ,String as , Bson in)
{
    Document document = new Document("input" , "$"+arrayName);

    if(as!=null && !as.isEmpty())
        document.append("as" , as);

    document.append("in" , in);

    return new Document("$map" , document);
}
}

也许很难理解我在这段代码上写的内容,您需要检查以下链接:

$map(aggregation)

$expr

$project(aggregation)

答案 1 :(得分:0)

$elemMatch投影运算符不能与2级嵌套数组一起使用。从嵌套数组的第二层获取匹配数组元素的方法是使用Aggregation Pipleline,如下所示:

db.collection.aggregate( [
  { $unwind: "$productList.products" },
  { $unwind: "$productList.products.plans" },
  { $match: { "productList.products.plans.type": "G" } },
] )

这将转换为以下 Java 代码:

MongoClient mongoClient = MongoClients.create("mongodb://localhost/");
MongoDatabase database = mongoClient.getDatabase("test");
MongoCollection<Document> collection = database.getCollection("collection");

List<Bson> pipeline = Arrays.asList(
                        unwind("$productList.products"), 
                        unwind("$productList.products.plans"), 
                        match(eq("productList.products.plans.type", "G"))
                      );                                    
List<Document> results = new ArrayList<>();
collection.aggregate(pipeline).into(results);   
results.forEach(doc -> System.out.println(doc.toJson()));