使用子字符串作为条件过滤子文档数组
Posted
技术标签:
【中文标题】使用子字符串作为条件过滤子文档数组【英文标题】:Filter sub-document array using substring as criteria 【发布时间】:2017-03-26 15:24:44 【问题描述】:我的收藏:
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);
);
;
我的问题:
如何搜索字符串的一部分? AFG101
应该返回属性为code
的所有子类别,其中包含字符串的任何部分。所以在这种情况下,AFG
会很受欢迎。与此 sql 问题相同:mysql: What is a reverse version of LIKE?
如何投影子类别。当前查询返回所有子类别。我只想返回那些击中的。
【问题讨论】:
我想如果你能在查询Mongo之前将AFG101
解析成对应的代码会更好。
@hyades,我不明白。我该怎么做?
我想我明白你在第一次提问时想要什么。您想要,例如:拥有多个文档,其中包含“code:AFG1”、“code;AFG2”、“code:AFG3”、“code:ABC1”。因此,当用户(无论)输入类似“AFG”mongo 的内容时将检索您在 CODE 字段中包含“AFG”的所有文档。如果是这样,请检查 mongodb 中的“regex”。docs.mongodb.com/manual/reference/operator/query/regex/…
@AlbertoRubio,不,相反。如果用户输入AFG101,并且我的文档中的属性代码有代码:AFG,应该返回这个。
嗯......它是一样的,不是吗? $regex 将搜索您键入的字符串并在您的文档中查找该字符串。这不完全是你想要的,但你可以从那里开始。或者,如果您知道所有代码都以 3 个相同的字符开头,请制作并拆分并仅从字符串中获取“AFG”。字符串的前 3 个字符。这就是我所拥有的,对不起。
【参考方案1】:
最好的方法是在 MongoDB 3.4 中使用$indexOfCP
字符串聚合运算符。
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"
]
]
您可以阅读我对类似问题1、2 和3 的其他答案。
从 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
【讨论】:
【参考方案2】:好吧,就像您的问题有两个部分一样,我可以想到两个单独的解决方案,但是我看不到将它们结合在一起的方法。
对于第一部分,$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"
])
但是我认为没有办法在聚合中进行反向正则表达式检查,但我也可能错了。此外,这种聚合代价高昂,因为有两个展开可能导致溢出内存限制以用于非常大的集合的聚合。
【讨论】:
【参考方案3】:你可以使用 $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
])
【讨论】:
你可以使用 $group : "_id" : "$_id"stage 和 $push 来制作数组以上是关于使用子字符串作为条件过滤子文档数组的主要内容,如果未能解决你的问题,请参考以下文章