mongoDB:如何获取嵌入文档的最新字段(非数组)

mongoDB:如何获取嵌入文档的最新字段(非数组),mongodb,mongodb-query,Mongodb,Mongodb Query,也许我在这里的想法是相反的,但我已经在消息线程中构建了数据 存在于文档中,所有消息都存在于嵌入文档中。(不是子文档数组) 我希望能够按照时间戳对嵌入的文档进行排序和限制 例如,第二个文档相当大,因此我只想检索最后10条消息(或w/e) 在鲍勃和我之间 { "_id" : ObjectId("2bjbkjb4234j134124"), "messages" : { "56a7b13f24236dea1247cdc7" : { "autho

也许我在这里的想法是相反的,但我已经在消息线程中构建了数据 存在于文档中,所有消息都存在于嵌入文档中。(不是子文档数组)

我希望能够按照时间戳对嵌入的文档进行排序和限制

例如,第二个文档相当大,因此我只想检索最后10条消息(或w/e) 在鲍勃和我之间

{ 
    "_id" : ObjectId("2bjbkjb4234j134124"), 
    "messages" : {
        "56a7b13f24236dea1247cdc7" : {
            "authorName" : "Nick", 
            "timestamp" : 1.453699391078E12, 
            "message" : "Hello"
        },
        ... 5 more messages
    }

},
{
    "_id" : ObjectId("3e11kjb4234j134172"), 
    "messages" : {
        "5727b13f24236dea1247ced8" : {
            "authorName" : "Bob", 
            "timestamp" : 1.2353453455078E12, 
            "message" : "Sup!"
        },
        ... 50,000 messages
    }

}
问题:


是否有一种方法可以在嵌入式文档(如上面的消息)上执行排序、限制和返回的等效操作

您真的应该在这里使用数组,因为使用命名对象键是非常困难的 与数据库的基本工作方式背道而驰

除了基本的查询问题,例如可能在集合中查找作者“Bob”的所有内容(这对于数组来说很简单),在查找“最后10个”时也会遇到类似的“暴力”匹配问题。更不用说,作为一个“非数组”,它变得非常主观,“最后十个”实际上是什么

即使以假设这些“键”实际上是MongoDB
ObjectID
值的相同生成值为例(因此是单调的且值总是递增的),计算这些排序顺序需要强制JavaScript处理,而不需要集合索引或自然数组索引位置的帮助:

db.collection.mapReduce(
函数(){
var messages=this.messages;
var newMessages=Object.keys(this.messages).sort().slice(-10).map(
功能(id){
返回消息[id];
}
);
emit(this._id,{“messages”:newMessages});
},
function(){},//这里没有真正减少任何内容
{“out”:{“inline”:1}
)
或者通过“timestamp”值(看起来不像时间戳)进行类似的处理,但这里的基本前提是将而不是数组的内容转换为数组,以便对结果进行排序并限制要返回的结果

基本上丑陋,而且设计非常糟糕。也就是说,使用mapReduce是改变返回文档结构的唯一方法(通过JavaScript处理)。该逻辑也可以在客户端中执行,其唯一优点是在通过网络连接发送之前剥离不需要的内容

使用数组会在“更新”内容上增加一些开销,这种想法也相当“胡说八道”。MongoDB从一开始就支持匹配位置更新,正确的结构和使用非常简单:

{
“_id”:ObjectId(“2bjbkjb4234j134124”),
“信息”:[
{
“_id”:“56a7b13f24236dea1247cdc7”,
“authorName”:“Nick”,
“时间戳”:1.453699391078E12,
“消息”:“你好”
},
//等
]
}
因此,如果要匹配和更新特定数组项(假设unqiue无处不在,但如果需要,只需调整为“每个文档”),只需在查询部分应用标识符,并在语句的“update”部分应用位置
$
运算符:

db.collection.update(
{“messages._id”:“56a7b13f24236dea1247cdc7”},
{“$set”:{
“messages.$.message”:“新事物”,
“messages.$.timestamp”:aNewValue
}}
)
使用
$push
将项添加到数组中还有一个优点,即默认情况下,所有“最新”项都添加到数组的末尾。因此,除非您更改此设置(并且不进行修改,因此需要最新的时间戳),否则您所需要做的就是
$slice
一个“已存在的数组”,而无需进一步处理:

db.collection.find(
{},
{“消息”:{“$slice”:-10}
)
如果您确实希望修改字段(如“timestamp”)来影响排序,那么您可以使用
$sort
修改器将
$push
以这种方式存储。这甚至可以通过简单的批量操作应用于修改后的数组元素:

var bulk=db.collection.initializeOrderedBulkOp();
//更新匹配的元素
bulk.find({
“_id”:ObjectId(“2bjbkjb4234j134124”),
“消息._id”:“56a7b13f24236dea1247cdc7”
}).updateOne({
“$set”:{
“messages.$.message”:“新事物”,
“messages.$.timestamp”:aNewValue
}
});
//按时间戳对数组排序
bulk.find({
“_id”:ObjectId(“2bjbkjb4234j134124”),
“消息._id”:“56a7b13f24236dea1247cdc7”
}).updateOne({
“$push”:{“消息”:{“$each”:[],“$sort”:{“时间戳”:1}}}
})
//从服务器发送和接收
bulk.execute();
虽然这实际上是两条update语句(因为您不能在一次更新操作中使用两条operator语句修改同一文档路径),但它仍然可以作为对服务器的单个请求和响应来执行,因此非常高效

当然,如果您不想永久存储订单,那么至少可以在聚合框架中操作数组,操作方式通常比通过mapReduce的JavaScript处理更有效:

db.collection.aggregate([
{“$match”:ObjectId(“2bjbkjb4234j134124”),
{“$unwind”:“$messages”},
{“$sort”:{“messages.timestamp”:-1}},//顺序与$limit相反
{“$limit”:10},
{“$组”:{
“\u id”:“$\u id”,
“消息”:{“$push”:“$messages”}
}}
])
甚至可以使用新的MongoDB 3.2运营商对多个文档进行超级幻想:

db.collection.aggregate([
{“$unwind”:“$messages”},
{“$sort”:{“_id”:1,“messages.timestamp”:1},
{“$组”:{
“\u id”:“$\u id”,
“消息”:{“$push”:“$messages”}
}},
{“$project”:{
“消息”:{“$slice”:[“$messages”,-10]}
}