mongodb子文档中的分页子文档

时间:2015-06-15 10:42:57

标签: mongodb mongodb-query

我想在Mongodb中分页我的数据。我使用slice运算符但无法分页我的数据。我希望带上我的行,但不能在这一行中进行分页。

我想只返回2行数据源。

如何解决它

我的查询:

db.getCollection('forms').find({
    "_id": ObjectId("557e8c93a6df1a22041e0879"),
    "Questions._id": ObjectId("557e8c9fa6df1a22041e087b")
}, {
    "Questions.$.DataSource": {
    "$slice": [0, 2]
    },
    "_id": 0,
    "Questions.DataSourceItemCount": 1
})

我的收集数据:

/* 1 */
{
    "_id" : ObjectId("557e8c93a6df1a22041e0879"),
    "QuestionCount" : 2.0000000000000000,
    "Questions" : [ 
        {
            "_id" : ObjectId("557e8c9ba6df1a22041e087a"),
            "DataSource" : [],
            "DataSourceItemCount" : NumberLong(0)
        }, 
        {
            "_id" : ObjectId("557e8c9fa6df1a22041e087b"),
            "DataSource" : [ 
                {
                    "_id" : ObjectId("557e9428a6df1a198011fa55"),
                    "CreationDate" : ISODate("2015-06-15T09:00:24.485Z"),
                    "IsActive" : true,
                    "Text" : "sdf",
                    "Value" : "sdf"
                }, 
                {
                    "_id" : ObjectId("557e98e9a6df1a1a88da8b1d"),
                    "CreationDate" : ISODate("2015-06-15T09:20:41.027Z"),
                    "IsActive" : true,
                    "Text" : "das",
                    "Value" : "asdf"
                }, 
                {
                    "_id" : ObjectId("557e98eea6df1a1a88da8b1e"),
                    "CreationDate" : ISODate("2015-06-15T09:20:46.889Z"),
                    "IsActive" : true,
                    "Text" : "asdf",
                    "Value" : "asdf"
                }, 
                {
                    "_id" : ObjectId("557e98f2a6df1a1a88da8b1f"),
                    "CreationDate" : ISODate("2015-06-15T09:20:50.401Z"),
                    "IsActive" : true,
                    "Text" : "asd",
                    "Value" : "asd"
                }, 
                {
                    "_id" : ObjectId("557e98f5a6df1a1a88da8b20"),
                    "CreationDate" : ISODate("2015-06-15T09:20:53.639Z"),
                    "IsActive" : true,
                    "Text" : "asd",
                    "Value" : "asd"
                }
            ],
            "DataSourceItemCount" : NumberLong(5)
        }
    ],
    "Name" : "er"
}

1 个答案:

答案 0 :(得分:1)

虽然这可能与一些真正的争论有关,但最好是更改文档结构以将数组条目“展平”为单个数组。主要原因是由于positional $ operator的当前限制,MongoDB在更新“内部”数组方面没有原子支持的“更新”。

无论如何,由于显而易见的原因,处理起来并不容易。

对于目前的结构,你可以这样做:

db.collection.aggregate([
    // Match the required document and `_id` is unique
    { "$match": {
        "_id":  ObjectId("557e8c93a6df1a22041e0879")
    }},

    // Unwind the outer array
    { "$unwind": "$Questions" },

    // Match the inner entry
    { "$match": {
        "Questions._id": ObjectId("557e8c9fa6df1a22041e087b"),
    }},

    // Unwind the inner array
    { "$unwind": "$Questions.DataSource" }

    // Find the first element
    { "$group": {
        "_id": {
            "_id": "$_id",
            "questionId": "$Questions._id"
       },
       "firstSource": { "$first": "$Questions.DataSource" },
       "sources": { "$push": "$Questions.DataSource" }
    }},

    // Unwind the sources again
    { "$unwind": "$sources" },

    // Compare the elements to keep
    { "$project": {
        "firstSource": 1,
        "sources": 1,
        "seen": { "$eq": [ "$firstSource._id", "$sources._id" ] }
    }},

    // Filter out anything "seen"
    { "$match": { "seen": true } },

    // Group back the elements you want
    { "$group": {
        "_id": "$_id",
        "firstSource": "$firstSource",
        "secondSource": { "$first": "$sources" }
    }}
])

这样就会给你那个内部数组的“前两个元素”。这是在聚合框架中实现$slice的基本过程, 是必需的,因为您不能以您尝试的方式使用带有“嵌套数组”的标准投影。

由于聚合框架不支持$slice,您可以看到执行“分页”将是一个非常可怕且“迭代”的操作,以便“摘取”数组元素。

我可以在这一点建议“扁平化”到单个数组,但同样的“切片”问题适用,因为即使你将“QuestionId”作为“内部”数据的属性,它也有相同的投影选择问题您需要相同的聚合方法。

然后,对于您的数据(对于某些查询操作),这个“看似”并不是很好的结构,但这完全取决于您的使用模式。这种结构适合这种类型的操作:

{
    "_id" : ObjectId("557e8c93a6df1a22041e0879"),
    "QuestionCount" : 2.0000000000000000,
    "Questions" : {
        "557e8c9ba6df1a22041e087a": {
            "DataSource" : [],
            "DataSourceItemCount" : NumberLong(0)
        }, 
        "557e8c9fa6df1a22041e087b": {
            "DataSource" : [ 
                {
                    "_id" : ObjectId("557e9428a6df1a198011fa55"),
                    "CreationDate" : ISODate("2015-06-15T09:00:24.485Z"),
                    "IsActive" : true,
                    "Text" : "sdf",
                    "Value" : "sdf"
                }, 
                {
                    "_id" : ObjectId("557e98e9a6df1a1a88da8b1d"),
                    "CreationDate" : ISODate("2015-06-15T09:20:41.027Z"),
                    "IsActive" : true,
                    "Text" : "das",
                    "Value" : "asdf"
                }
            ],
            "DataSourceItemCount" : NumberLong(5)
        }
    }
}

这有效:

db.collection.find(
    { 
        "_id": ObjectId("557e8c93a6df1a22041e0879"),
        "Questions.557e8c9fa6df1a22041e087b": { "$exists": true }
    },
    { 
        "_id": 0,
        "Questions.557e8c9fa6df1a22041e087b.DataSource": { "$slice": [0, 2] },
        "Questions.557e8c9fa6df1a22041e087b.DataSourceItemCount": 1
    }
)

嵌套数组对于许多操作来说并不是很好,特别是更新操作,因为无法获得更新操作的“内部”数组索引。位置$运算符只会获得“第一个”或“外部”数组索引,并且不能“也”匹配内部数组索引。

使用类似结构的更新涉及“整体”阅读文档,然后在代码中进行操作并写回。没有“保证”文件在这些操作之间的集合中没有变化,除非妥善处理,否则可能导致不一致。

另一方面,如图所示,修改后的结构适用于给定的查询类型,但如果您需要动态搜索或“聚合”您所代表的“外部”,则可能会“糟糕”问题。”

使用MongoDB的数据结构对于“如何使用它”非常主观。因此,最好在为应用程序“确定”最终数据结构设计之前考虑所有使用模式。

因此,您可以注意到所提到的问题和解决方案,或者只是通过标准的“位置”匹配检索“外部”元素,然后在客户端代码中“切片”。

这都是“最适合您应用的问题”。