mongodb 子文档中的分页子文档

paging subdocument in mongodb subdocument

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

我想return只有2行数据源。

如何解决

我的查询:

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

我的 collection 数据:

/* 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"
}

虽然这可能需要一些真正的争论,但您最好将文档结构更改为 "flatten" 将数组条目合并到一个数组中。这样做的主要原因是 "updates" 由于 positional $ operator.[=18= 的当前限制,MongoDB 在更新 "inner" 数组方面不提供原子支持。 ]

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

对于目前的结构,您可以这样处理:

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" }
    }}
])

所以这将为您提供该内部数组的 "first two elements"。这是在聚合框架中实现 $slice 的基本过程, 所必需的,因为您不能以您尝试的方式使用带有 "nested array" 的标准投影。

由于聚合框架不支持 $slice,您可以看到 "paging" 将是一个非常可怕的操作,"iterative" 操作是为了 "pluck"数组元素。

此时我可以建议 "flattening" 到单个数组,但同样的 "slicing" 问题仍然存在,因为即使你将 "QuestionId" 设为 属性 =50=] 数据,它具有相同的投影和选择问题,您需要相同的聚合方法。

还有一个 "seemingly" 对您的数据来说不是很好的结构(对于某些查询操作),但这完全取决于您的使用模式。这种结构适合这种类型的操作:

{
    "_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
    }
)

嵌套数组不适用于许多操作,尤其是更新操作,因为无法为更新操作获取 "inner" 数组索引。位置 $ 运算符只能获取 "first" 或 "outer" 数组索引,不能 "also" 匹配内部数组索引。

像您这样的结构更新涉及 "reading" 整个文档,然后在代码中进行操作并回写。没有 "guarantee" 文档在这些操作之间的集合中没有更改,除非处理得当,否则可能导致不一致。

另一方面,如图所示的修订结构适用于给定的查询类型,但如果您需要动态搜索,则可能 "bad" 或 "aggregate" 您所代表的内容作为 "outer" "Questions".

MongoDB 的数据结构对 "how you use it" 非常主观。因此,最好在 "nailing down" 应用程序的最终数据结构设计之前考虑所有使用模式。

因此您可以记下所指出的问题和解决方案,或者简单地接受通过标准 "positional" 匹配检索 "outer" 元素,然后在您的 "slice"客户端代码。

都是"what suits your application best"的事情。