【问题标题】:Mongodb Aggregation : How to return only matching elements of an array [duplicate]Mongodb聚合:如何仅返回数组的匹配元素[重复]
【发布时间】:2012-08-31 02:08:39
【问题描述】:

在我的 mongoDB 图书集中,我的文档结构如下:

/* 0 */
{
  "_id" : ObjectId("50485b89b30f1ea69110ff4c"),

  "publisher" : {
    "$ref" : "boohya",
    "$id" : "foo"
  },
  "displayName" : "Paris Nightlife",
  "catalogDescription" : "Some desc goes here",
  "languageCode" : "en",
  "rating" : 0,
  "status" : "LIVE",
  "thumbnailId" : ObjectId("50485b89b30f1ea69110ff4b"),
  "indexTokens" : ["Nightlife", "Paris"]
}

我执行以下正则表达式查询以查找具有一个以“Par”开头的 indexToken 的所有文档:

{ "indexTokens" : { "$regex" : "^Par" , "$options" : "i"}}

如果我只选择要像这样返回的 indexTokens 字段:

{ "indexTokens" : 1}

生成的 DBObject 是

{ "_id" : { "$oid" : "50485b89b30f1ea69110ff4c"} , "indexTokens" : [ "Nightlife" , "Paris"]}

我想得到的只是与正则表达式匹配的令牌/标签(此时我不关心检索文档,我也不需要匹配文档的所有标签)

这是在 MongoDB v2.2 下发布的新聚合框架的情况。 ?

如果是,我该如何修改我的查询以使实际结果如下所示:

{ "indexTokens" : ["Paris", "Paradise River", "Parma" 等 ....]}

额外问题(你有 codez):我如何使用 Java 驱动程序来做到这一点?

现在我的 java 看起来像:

DBObject query = new BasicDBObject("indexTokens", java.util.regex.Pattern.compile("^"+filter+"", Pattern.CASE_INSENSITIVE));
    BasicDBObject fields = new BasicDBObject("indexTokens",1);
    DBCursor curs = getCollection()
                    .find(query, fields)
                    .sort( new BasicDBObject( "indexTokens" , 1 ))
                    .limit(maxSuggestionCount);

谢谢 :)

编辑:

根据你的回答,我修改了我的 JAVA 代码如下:

BasicDBObject cmdBody = new BasicDBObject("aggregate", "Book"); 
    ArrayList<BasicDBObject> pipeline = new ArrayList<BasicDBObject>(); 

    BasicDBObject match = new BasicDBObject("$match", new BasicDBObject("indexTokens", java.util.regex.Pattern.compile("^"+titleFilter+"", Pattern.CASE_INSENSITIVE)));
    BasicDBObject unwind = new BasicDBObject("$unwind", "$indexTokens");
    BasicDBObject match2 = new BasicDBObject("$match", new BasicDBObject("indexTokens", java.util.regex.Pattern.compile("^"+titleFilter+"", Pattern.CASE_INSENSITIVE)));
    BasicDBObject groupFilters = new BasicDBObject("_id",null);
    groupFilters.append("indexTokens", new BasicDBObject( "$push", "$indexTokens"));
    BasicDBObject group = new BasicDBObject("$group", groupFilters);

    pipeline.add(match);
    pipeline.add(unwind);
    pipeline.add(match2);
    pipeline.add(group);

    cmdBody.put("pipeline", pipeline); 



    CommandResult res = getCollection().getDB().command(cmdBody);
    System.out.println(res);

哪些输出

{ "result" : [ { "_id" :  null  , "indexTokens" : [ "Paris"]}] , "ok" : 1.0}

这真是天才!

非常感谢!

【问题讨论】:

    标签: mongodb aggregation-framework


    【解决方案1】:

    您可以使用 2.2 聚合框架来做到这一点。像这样的东西;

    db.books.runCommand("aggregate", {
        pipeline: [
            {   // find docs that contain Par*
                $match: { "indexTokens" : { "$regex" : "^Par" , "$options" : "i"}},
            },
            {   // create a doc with a single array elemm for each indexToken entry
                $unwind: "$indexTokens" 
            },
            {   // now produce a list of index tokens
                $group: {
                    _id: "$indexTokens",
                },
            },
        ],
    })
    

    或者如果你真的想要没有文档的数组,这可能更接近你所追求的;

    db.books.runCommand("aggregate", {
        pipeline: [
            {   // find docs that contain Par*
                $match: { "indexTokens" : { "$regex" : "^Par" , "$options" : "i"}},
            },
            {   // create a doc with a single array elemm for each indexToken entry
                $unwind: "$indexTokens" 
            },
            {   // now throw out any unwind's that DON'T contain Par*
                $match: { "indexTokens": { "$regex": "^Par", "$options": "i" } },
            },
            {   // now produce the list of index tokens
                $group: {
                    _id: null,
                    indexTokens: { $push: "$indexTokens" },
                },
            },
        ],
    })
    

    【讨论】:

    • 您可以将此添加到您的原始答案中作为第二种解决方案。这样人们就不会混淆为什么你有两个答案:)
    • 感谢你们两个,它就像一个魅力。我添加了一个答案来展示我是如何在 JAVA 中做到这一点的(我没有最新的驱动程序,所以我不能在 DBCollection 上使用 aggregate() 方法。
    【解决方案2】:

    基于 cirrus 的响应,我建议先执行 $unwind 以避免多余的 $match。比如:

    db.books.aggregate(
        {$unwind:"$indexTokens"},
        {$match:{indexTokens:/^Par/}},
        {$group:{_id:null,indexTokens:{$push:"$indexTokens"}}
    })
    

    你如何在 Java 中做到这一点?您可以使用 MongoDB v2.9.0 驱动程序的DBCollection.aggregate(...) 方法。每个管道运营商,例如。 $unwind$match,对应一个DBObject对象。

    【讨论】:

    • 其实我不认为 $match 是多余的。 $unwind 的问题在于它必须在 RAM 中创建大量文档,并且您希望尽早减少该集合。第一个 $match 确保我们只使用在我们展开之前在 indexTokens 中有 Par* 的文档。然后第二个 $match 将设置减少到我们想要的设置。请记住,您希望尽早获得 $match 以减少管道量。
    • 你是对的。剔除不匹配的文档,展开数组,然后再次匹配,剔除不匹配正则表达式的文档。
    猜你喜欢
    • 2015-05-27
    • 1970-01-01
    • 2016-01-26
    • 2016-05-20
    • 2021-07-28
    • 2014-10-14
    • 2018-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多