使用子字符串作为条件过滤子文档数组

时间:2016-11-12 15:09:53

标签: mongodb mongoose mapreduce aggregation-framework

我的收藏:

{
  title: 'Computers',
  maincategories:[
    {
       title: 'Monitors',
       subcategories:[
         {
            title: '24 inch',
            code: 'AFG'
         }
       ]
    }
  ]
}

我想查询代码。代码只是第一部分,所以我想拥有包含给定搜索的所有子类别。所以AFG101将返回此子类别。

我的查询:

module.exports = (req, res) => {
  var q = {
    'maincategories.subcategories': {
      $elemMatch: {
        code: 'AFG101'
      }
    }
  };

  var query = mongoose.model('TypeCategory').find(q, {'maincategories.$': 1, 'title': 1});

  query.exec((err, docs) => {
    res.status(200).send(docs);
  });
};

我的问题:

  1. 如何搜索字符串的一部分? AFG101应返回包含字符串任何部分的属性code的所有子类别。所以在这种情况下,AFG会很受欢迎。与此sql问题相同:MySQL: What is a reverse version of LIKE?

  2. 如何投影子类别。当前查询返回所有子类别。我只想回报那些击球。

3 个答案:

答案 0 :(得分:1)

执行此操作的最佳方法是使用$indexOfCP字符串聚合运算符在MongoDB 3.4中。

let code = "afg101";

db.collection.aggregate([
    { "$project": { 
        "title": 1, 
        "maincategories": { 
            "$map": { 
                "input": "$maincategories", 
                "as": "mc", 
                "in": { 
                    "$filter": { 
                        "input": "$$mc.subcategories", 
                        "as": "subcat", 
                        "cond": { 
                            "$gt": [ 
                                { 
                                    "$indexOfCP": [ 
                                        code, 
                                        { "$toLower": "$$subcat.code" }
                                     ] 
                                }, 
                               -1 
                            ] 
                        } 
                    } 
                } 
            } 
        } 
    }} 
])

返回:

{
    "_id" : ObjectId("582cba57e6f570d40d77b3a8"),
    "title" : "Computers",
    "maincategories" : [
        [
            {
                "title" : "24 inch",
                "code" : "AFG"
            }
        ]
    ]
}

您可以阅读我对类似问题123的其他答案。

从3.2向后,唯一的方法是使用mapReduce

db.collection.mapReduce(
    function() { 
        var code = 'AFG101'; 
        var maincategories = this.maincategories.map(function(sdoc) {
            return { 
                "title": sdoc.title, 
                "subcategories": sdoc.subcategories.filter(function(scat) { 
                    return code.indexOf(scat.code) != -1; 
                }
            )};
        }); 
        emit(this._id, maincategories); 
   }, 
   function(key, value) {}, 
   { "out": { "inline": 1 } 
})

产生类似这样的东西:

{

    "results" : [
        {
            "_id" : ObjectId("582c9a1aa358615b6352c45a"),
            "value" : [
                {
                    "title" : "Monitors",
                    "subcategories" : [
                        {
                            "title" : "24 inch",
                            "code" : "AFG"
                        }
                    ]
                }
            ]
        }
    ],
    "timeMillis" : 15,
        "counts" : {
            "input" : 1,
            "emit" : 1,
            "reduce" : 0,
            "output" : 1
        },
    "ok" : 1
}

答案 1 :(得分:0)

好吧,就像你的问题有两个部分一样,我可以想到两个独立的解决方案,但我没有看到将它们连接在一起的方法。

对于第一部分$ where可用于执行反向正则表达式,但它很脏,它是一个过度杀手而且它不能使用任何索引,因为$ where运行在每个文档上。

db.TypeCategory.find({$where:function(){for(var i  in this.maincategories)
{for(var j in this.maincategories[i].subcategories)
 {if("AFG101".indexOf(this.maincategories[i].subcategories[j].code)>=0)
  {return true}}}}},{"maincategories.subcategories.code":1})

即使使用此选项,也需要进行几次边界检查,并且无法投影两级嵌套数组。 MongoDB不支持此类投影。

为此目的,我们可能会进行聚合

db.TypeCategory.aggregate([{$unwind:"$maincategories"},
 {$unwind:"$maincategories.subcategories"},
 {$match:{"maincategories.subcategories.code":"AFG"}},
 {$group:{_id:"$_id","maincategories":{$push:"$maincategories"}}}
])

但是我不认为有一种方法可以在聚合中进行反向正则表达式检查,但我也可能错了。此聚合也是昂贵的,因为有两个unwinds可能导致溢出内存限制以便为真正大的集合进行聚合。

答案 2 :(得分:0)

您可以使用$ substr并执行此操作

db.getCollection('cat').aggregate([
          {"$unwind" : "$maincategories"},
          {"$unwind" : "$maincategories.subcategories"},
          {"$project" :
                {"maincategories" : 1,
                "title":1,"sub" : {"$substr" :["$maincategories.subcategories.code",0,3]}}},
         {"$match" : {"sub" : "AFG"}},
         {"$project" : 
                 {"maincategories" : 1,
                  "title":1}
          }
    ])