根据您的需求,其中一种方法是设计模式,使每个文档具有容纳多个文档的能力,并且本身充当一个封顶容器。
{
"_id":Number,
"doc":Array
}
每个文档集合将充当一个有限容器,文档将以数组形式存储在“doc”字段中。由于“doc”字段是一个数组,因此它将保持插入顺序。您可以将文档数量限制为“n”。因此每个容器文档的“_id”字段将按“n”递增,表示容器文档可以容纳的文档数。
通过这样做,您可以避免向文档添加额外的字段、额外的索引和不必要的排序。
插入第一条记录时(即文档集合为空时)。
var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});
插入后续记录
- 确定最后一个容器文档的
_id
和它所包含的文档数量number
。
- 如果它包含的文档数量小于
n
,则更新容器文档,并添加新文档;否则,创建一个新的容器文档。
例如,每个容器文档
最多可以包含5
个文档,我们想要插入一个新的文档。
var record = {"name" : "newlyAdded"};
db.col.aggregate( [ {
$group : {
"_id" : null,
"max" : {
$max : "$_id"
},
"lastDocSize" : {
$last : "$doc"
}
}
}, {
$project : {
"currentMaxId" : "$max",
"capSize" : {
$size : "$lastDocSize"
},
"_id" : 0
}
} ]).forEach( function(check) {
if (check.capSize < 5) {
print("updating");
db.col.update( {
"_id" : check.currentMaxId
}, {
$push : {
"doc" : record
}
});
} else {
print("inserting");
db.col.insert( {
"_id" : check.currentMaxId + 5,
"doc" : [ record ]
});
}
})
请注意,
聚合
是在服务器端运行的非常高效的操作,还要注意,在版本
2.6
之前,
聚合
将返回一个
文档而不是一个
游标。因此,您需要修改上面的代码,仅从单个文档中进行选择而不是迭代游标。
在文档之间插入新文档
现在,如果您想在文档
1
和
2
之间插入一个新文档,我们知道该文档应该位于具有
_id=0
的容器内,并且应该放置在该容器的
doc
数组的
second
位置。
因此,我们利用
$each
和
$position
操作符来插入到特定位置。
var record = {"name" : "insertInMiddle"};
db.col.update(
{
"_id" : 0
}, {
$push : {
"doc" : {
$each : [record],
$position : 1
}
}
}
);
处理溢出
现在,我们需要处理每个容器中的文档溢出问题,比如在_id=0
的容器中插入一个新文档。如果该容器已经有5
个文档,我们需要将最后一个文档移动到下一个容器
,并一直这样做,直到所有容器都容纳了不超过其容量的文档,如果必要,我们需要创建一个容器来容纳溢出的文档。
这个复杂的操作应该在服务器端完成。为了处理这个问题,我们可以创建一个脚本,例如下面的脚本,并将其注册
到mongodb中。
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;
if (count <= 5)
return;
else {
print("updating collection: " + id);
var record = currDocArr.splice(currDocArr.length - 1, 1);
db.col.update( {
"_id" : nextColId
}, {
$push : {
"doc" : {
$each : record,
$position : 0
}
}
});
db.col.update( {
"_id" : id
}, {
"doc" : currDocArr
});
handleOverFlow(nextColId);
}
}
为了使得每次在中间插入时,我们可以通过传递容器 ID handleOverFlow(containerId)
调用该函数。
按顺序获取所有记录
只需在聚合管道中使用 $unwind
运算符即可。
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,以便在排序后,项目的顺序将会改变。
根据它们的_id对数组进行排序。
docArray.sort( function(a, b) {
return a._id - b._id;
});
使用新的文档数组更新带有容量限制的容器。
但最终,一切都取决于哪种方法最可行并最符合您的要求。
回答您的问题:
在MongoDB中存储一组文档的好方法是什么,在此过程中顺序很重要?我需要轻松地在任意位置插入文档,并可能在以后重新排序它们。
使用文档数组。
比如说,我想在序列为5的元素和序列为6的元素之间插入一些内容怎么办?
在db.collection.update()
函数中使用$each
和$position
操作符,就像我的回答中所示。
我对数据库管理的理解有限告诉我,这样的查询会变慢,通常不是一个好主意,但我很乐意接受纠正。
是的。除非集合数据非常少,否则会影响性能。
我可以使用带有保证顺序的固定大小的集合,但如果需要扩展集合,则会遇到问题。(再一次,我可能也是错的。)
是的。使用固定大小的集合可能会丢失数据。