仅当至少3列具有非空值时,如何在mongodb中获取文档

时间:2014-06-24 10:47:42

标签: mongodb mongodb-query

我在MongoDB中有一个集合,说具有属性id,名称,标准,标记,平均值的STUDENT。现在我想写一个查询,这样我只得到那些至少有3个属性包含非空值的文档。

所有那些在(名称,标准,标记)或(名称,标记,平均值)或(名称,标准,标记,平均值)或(ID,名称,标准,标记,平均值)中包含非空值的文档应该打印。但是,如果任何文档仅包含(name,standard)为非null或(标准,标记),则应忽略。

1 个答案:

答案 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的。好吧,至少在达到“三”的数量之前。

这给你一些方法。希望第一个真的适合。