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"的事情。
我想在 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"的事情。