我在MongoDB中有一个集合,说具有属性id,名称,标准,标记,平均值的STUDENT。现在我想写一个查询,这样我只得到那些至少有3个属性包含非空值的文档。
所有那些在(名称,标准,标记)或(名称,标记,平均值)或(名称,标准,标记,平均值)或(ID,名称,标准,标记,平均值)中包含非空值的文档应该打印。但是,如果任何文档仅包含(name,standard)为非null或(标准,标记),则应忽略。
答案 0 :(得分:0)
我会说这样的“学生”文件是这样的:
{ name: "a", standard: "b", marks: 10 },
{ name: "b", marks: 5, average: 2 },
{ id: 2, name: "c", marks: 10, average: 7 },
{ name: "c", standard: "b" },
{ standard: "c", marks: 3 }
然后“理想地”你会做这样的事情:
db.students.find({
"$or": [
{
"$and": [
{ "name": { "$exists": true } },
{ "name": { "$ne": null } },
{ "standard": { "$exists": true } },
{ "standard": { "$ne": null } },
{ "marks": { "$exists": true } },
{ "marks": { "$ne": null } },
],
},
{
"$and": [
{ "name": { "$exists": true } },
{ "name": { "$ne": null } },
{ "marks": { "$exists": true } },
{ "marks": { "$ne": null } },
{ "average": { "$exists": true } },
{ "average": { "$ne": null } }
],
},
{
"$and": [
{ "name": { "$exists": true } },
{ "name": { "$ne": null } },
{ "marks": { "$exists": true } },
{ "marks": { "$ne": null } },
{ "standard": { "$exists": true } },
{ "standard": { "$ne": null } },
{ "average": { "$exists": true } },
{ "average": { "$ne": null } }
],
},
{
"$and": [
{ "id": { "$exists": true } },
{ "id": { "$ne": null } },
{ "name": { "$exists": true } },
{ "name": { "$ne": null } },
{ "marks": { "$exists": true } },
{ "marks": { "$ne": null } },
{ "standard": { "$exists": true } },
{ "standard": { "$ne": null } },
{ "average": { "$exists": true } },
{ "average": { "$ne": null } }
],
}
]
})
其中不包括最后两个文件。
同样在现代MongoDB 2.6及更高版本中,您可以获得索引交集,或者考虑$or
操作数的2.4版本的版本。所以你可以像这样索引:
db.student.ensureIndex({ "name": 1, "standard": 1, "marks": 1 })
db.student.ensureIndex({ "name": 1, "marks": 1, "average": 1 })
db.student.ensureIndex({ "name": 1, "marks": 1, "standard": 1, "average": 1 })
db.student.ensureIndex({ "id": 1, "name": 1, "marks": 1, "standard": 1, "average": 1 })
这可能会增加很多“索引”空间的使用,所以在这种情况下,手段可能超过目的。
当然,为了更灵活地确定这个方法(虽然不是那么快),你可以接近聚合框架:
db.students.aggregate([
{ "$project": {
"id": { "$ifNull": [ "$id", null ] },
"name": { "$ifNull": [ "$name", null ] },
"marks": { "$ifNull": [ "$marks", null ] },
"standard": { "$ifNull": [ "$standard", null ] },
"average": { "$ifNull": [ "$average", null ] },
"fields": {
"$add": [
{ "$cond": [ { "$ifNull": [ "$id", null ] }, 1, 0 ] },
{ "$cond": [ { "$ifNull": [ "$name", null ] }, 1, 0 ] },
{ "$cond": [ { "$ifNull": [ "$marks", null ] }, 1, 0 ] },
{ "$cond": [ { "$ifNull": [ "$standard", null ] }, 1, 0 ] },
{ "$cond": [ { "$ifNull": [ "$average", null ] }, 1, 0 ] },
]
}
}},
{ "$match": { "fields": { "$gte": 3 } } }
])
如果受到实际需要声明可能的那些“字段”的所有的聚合框架约束的限制,那么对于您的问题,这基本上是更“文字”的解释。
$ifNull
运算符是执行“繁重工作”的运算符,通过将“不存在”或null
字段替换为null
值进行评估。您还可以“尝试”在初始管道阶段中使用$match
进行过滤,就像在第一个查询中减少输入一样。
如果您只有**太多*不同的字段组合以指定任何一种形式,并且您只需要知道“三个”或更多字段基本上存在或不为空,那么最终真正的问题就出现了。
这种方法归结为使用$where
形式的评估,这是处理常规查询的最低效方式,但它是最灵活的,因为JavaScript代码可以处理这些情况:
db.students.find(
function() {
var count = 0;
for ( var k in this ) {
if ( ( k != null) && ( k != "_id") ) {
count++;
if ( count >= 3 )
break;
}
}
return ( count >= 3 );
}
)
因此,虽然最后一个形式“看起来”很简单,但它实际上非常可怕,因为没有办法避免本质上最终作为“完整集合扫描”,因为每个文档中的所有字段都会被评估为JavaScript的。好吧,至少在达到“三”的数量之前。
这给你一些方法。希望第一个真的适合。