在订单很重要的MongoDB中存储一组文档的好方法是什么?我需要轻松地将文档插入任意位置,并可能在以后重新排序。
我可以为每个项目分配一个不断增加的数字并按其排序,或者我可以按_id
排序,但我不知道如何在其他文档之间插入另一个文档。假设我想在sequence
5
的元素和sequence
6
的元素之间插入内容?
我的第一个猜测是增加以下所有元素的sequence
,以便使用类似db.items.update({"sequence":{$gte:6}}, {$inc:{"sequence":1}})
的查询为新元素留出空间。我对数据库管理的有限理解告诉我,像这样的查询会很慢并且通常是一个坏主意,但我很高兴能够得到纠正。
我想我可以将新元素sequence
设置为5.5
,但我认为这会很快变得混乱。 (再次,如果我错了,请纠正我。)
我可以使用带有保证订单的上限集合,但如果我需要增加集合,我会遇到问题。 (再说一遍,我也可能错了。)
我可以让每个文档都包含对下一个文档的引用,但这需要查询列表中的每个项目。 (你得到一个项目,将其推送到结果数组,然后根据当前项目的next
字段获取另一个项目。)除了明显的性能问题,我也无法通过将mongo游标排序到我的{#each}
空格键块表达式,并在数据库更改时让它实时更新。 (我正在使用Meteor全栈javascript框架。)
我知道一切都有它的优点和缺点,我可能只需要使用上面列出的一个选项,但我想知道是否有更好的方法来做事。
答案 0 :(得分:4)
根据您的要求,其中一种方法可能是设计您的架构,使每个文档具有来容纳多个文档,并且本身充当加盖容器。
{
"_id":Number,
"doc":Array
}
集合中的每个文档都将充当上限容器,文档将作为数组存储在doc
字段中。作为数组的doc
字段将保持插入顺序。
您可以将文档数限制为n
。因此,每个容器文档的_id
字段将按n
递增,表示容器文档可以容纳的文档数。
通过执行此操作,您避免将extra fields
添加到文档extra indices
,unnecessary sorts
。
即收集时为空。
var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});
_id
和number
它持有的文件。n
,则更新
包含新文档的容器文档,否则创建新容器
文档。说,每个container document
最多可以包含5
个文档,我们想要插入一个新文档。
var record = {"name" : "newlyAdded"};
// using aggregation, get the _id of the last inserted container, and the
// number of record it currently holds.
db.col.aggregate( [ {
$group : {
"_id" : null,
"max" : {
$max : "$_id"
},
"lastDocSize" : {
$last : "$doc"
}
}
}, {
$project : {
"currentMaxId" : "$max",
"capSize" : {
$size : "$lastDocSize"
},
"_id" : 0
}
// once obtained, check if you need to update the last container or
// create a new container and insert the document in it.
} ]).forEach( function(check) {
if (check.capSize < 5) {
print("updating");
// UPDATE
db.col.update( {
"_id" : check.currentMaxId
}, {
$push : {
"doc" : record
}
});
} else {
print("inserting");
//insert
db.col.insert( {
"_id" : check.currentMaxId + 5,
"doc" : [ record ]
});
}
})
请注意,aggregation
在服务器端运行且效率很高,同时请注意aggregation
会返回文档而不是游标版本previous to 2.6
。因此,您需要修改上述代码,只需从单个文档中进行选择,而不是迭代游标。
现在,如果您想在文档1
和2
之间插入新文档,我们知道该文档应该位于具有_id=0
的容器内,并且应该放在second
位于该容器的doc
数组中。
因此,我们会使用$each
和$position
运算符插入特定位置。
var record = {"name" : "insertInMiddle"};
db.col.update(
{
"_id" : 0
}, {
$push : {
"doc" : {
$each : [record],
$position : 1
}
}
}
);
现在,我们需要处理每个overflowing
中的文档container
,比如我们在_id=0
的容器中插入一个新文档。如果容器已经有5
个文档,我们需要move the last document to the next container
并执行此操作,直到所有容器都保存其容量内的文档,如果最终需要,我们需要创建一个容器来保存溢出的文档。 / p>
为了解决这个问题,我们可以使用mongodb创建一个脚本,例如下面的脚本和register
。
db.system.js.save( {
"_id" : "handleOverFlow",
"value" : function handleOverFlow(id) {
var currDocArr = db.col.find( {
"_id" : id
})[0].doc;
print(currDocArr);
var count = currDocArr.length;
var nextColId = id + 5;
// check if the collection size has exceeded
if (count <= 5)
return;
else {
// need to take the last doc and push it to the next capped
// container's array
print("updating collection: " + id);
var record = currDocArr.splice(currDocArr.length - 1, 1);
// update the next collection
db.col.update( {
"_id" : nextColId
}, {
$push : {
"doc" : {
$each : record,
$position : 0
}
}
});
// remove from original collection
db.col.update( {
"_id" : id
}, {
"doc" : currDocArr
});
// check overflow for the subsequent containers, recursively.
handleOverFlow(nextColId);
}
}
因此after every insertion in between
我们可以通过传递容器ID function
来调用此handleOverFlow(containerId)
。
只需使用$unwind
中的aggregate pipeline
运算符。
db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);
您可以将每个文档存储在带有“_id”字段的上限容器中:
.."doc":[{"_id":0,","name":"xyz",...}..]..
抓住您想要的上限容器的“doc”数组 重新订购商品。
var docArray = db.col.find({"_id":0})[0];
更新他们的ID,以便在排序后项目的顺序会发生变化。
根据数组_ids对数组进行排序。
docArray.sort( function(a, b) {
return a._id - b._id;
});
使用新的doc数组更新加盖的容器。
但话又说回来,一切都归结为哪种方法可行并且最适合您的要求。
回答你的问题:
什么是在MongoDB中存储一组文档的好方法,其中顺序很重要?我需要轻松地在任意文件中插入文档 位置,可能会在以后重新排序。
文档作为数组。
我想在一个序列为5的元素和一个序列为6的元素之间插入一些东西?
使用$each
函数中的$position
和db.collection.update()
运算符,如我的回答所示。
我对数据库管理的有限理解告诉我一个 像这样的查询会很慢并且通常是一个坏主意,但我很高兴 待纠正。
是。它会影响性能,除非集合的数据非常少。
我可以使用带有保证订单的上限集合,但如果我需要增加集合,那么我会遇到问题。 (然而 再说一次,我也可能错了。)
是。使用上限集合,您可能会丢失数据。
答案 1 :(得分:1)
对于任何集合的任意排序,您需要一个字段来对其进行排序。我称之为“序列”。
schema:
{
_id: ObjectID,
sequence: Number,
...
}
db.items.ensureIndex({sequence:1});
db.items.find().sort({sequence:1})
答案 2 :(得分:1)
MongoDB中的_id
字段是一个唯一的索引键,类似于关系数据库中的主键。如果文档中存在固有顺序,理想情况下,您应该能够将唯一键与每个文档相关联,并且键值反映顺序。因此,在准备要插入的文档时,请明确添加_id
字段作为此键(如果不这样做,mongo会自动使用BSON objectid创建它)。
就检索结果而言,MongoDB does not guarantee the order of return documents unless you explicitly use .sort()
。如果您不使用.sort()
,结果通常会按自然顺序(插入顺序)返回。再次,无法保证此行为。
我建议您在插入时用您的订单覆盖_id
,并在检索时使用排序。由于_id
是必需且自动索引的实体,因此您不会浪费任何定义排序键的空间,并为其存储索引。
答案 3 :(得分:-1)
以下是一些可能相关的常规排序数据库答案的链接:
我建议使用Floating point
解决方案-添加一个position
列:
在位置列中使用浮点数。 然后,您可以重新排序列表,仅更改“已移动”行中的位置列。 如果您的用户希望将“红色”放置在“蓝色”之后但在“黄色”之前,那么您只需要计算
red.position = ((yellow.position - blue.position) / 2) + blue.position
几百万个重新定位后,您可能会得到很小的浮点数,以至于没有“介于”之间的浮点数,但这与看到独角兽的可能性差不多。
检索它时,您只需说col.sort()
就可以对其进行排序,而无需任何客户端代码(例如在链接列表解决方案中)