我会说给定这样的“学生”文件:
{ 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。至少在数到“三”之前。
这为您提供了一些方法。希望第一个确实适合。