【问题标题】:How to get a document in mongodb only if at least 3 columns have non null values仅当至少 3 列具有非空值时,如何在 mongodb 中获取文档
【发布时间】:2014-06-24 23:32:01
【问题描述】:

我在 MongoDB 中有一个集合,说 STUDENT,其属性为 id、name、standard、marks、average。现在我想编写一个查询,以便只获取至少 3 个属性包含非空值的文档。

所有那些在 (name,standard,marks) 或 (name,marks,average) 或 (name,standard,marks,average) 或 (id, name, standard, Marks, average) 中包含非空值的文档都应该被打印。但如果任何文档仅包含 (name,standard) 为非 null 或 (standard,marks) 应被忽略。

【问题讨论】:

  • 这需要在您的问题中提供更清晰的示例。包括一些符合和不符合您预期结果的条件的示例文档。
  • 现在清楚了吗?
  • 如果没有一些物理逻辑,无论是目前使用聚合框架还是其他一些方法,您都无法做到这一点。 MongoDB 无法在查询结果中报告匹配的列数。我认为稀疏索引也不会在这里工作
  • 实际的文档示例将使更广泛的受众更清楚。请记住,不是每个人都能看到您所看到的。您的工作是向不在您办公桌前工作的世界清楚地展示您的案例。
  • 那么,4 $or 语句就可以了

标签: mongodb mongodb-query


【解决方案1】:

我会说给定这样的“学生”文件:

{ 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。至少在数到“三”之前。

这为您提供了一些方法。希望第一个确实适合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-09
    • 2018-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多