编辑:解决方案3(我正在使用的内容)
db.update(
{
_id: ObjectId("..playerId..."),
"OutstandingRewards.Id": {$all:[Outstanding, Ids, From PHP]}
},
{
$pull: {"OutstandingRewards": {"Id": {$in:[Outstanding, Ids, From PHP]}}},
$addToSet: {"GameObjects":{$each:[New, Rewards, From, PHP]}}
},
);
此原子更新可确保我希望在我OutstandingRewards[]
删除它们时将所有我想要声明的优秀ID仍然存在,并将相关的已处理奖励添加到GameObjects[]
。此外,使用$addToSet
运算符可确保不会在GameObjects[]
中插入重复的条目。
解决方案基于Neil Lunn的答案如下!
原创Qn:
为具有可索赔奖励的游戏编写数据库,数据结构如下:
.
.
"OutstandingRewards" : [
{
"Type" : 0,
"Id" : ObjectId("53c7b54f12727064000000a0")
},
{
"Type" : 1,
"Id" : ObjectId("53c7b54f12727064000000a2")
},
],
"GameObjects":[
{
"Type" : 1,
"Id" : ObjectId("53c7b54f12727064000000a5")
},
{
"Type" : 3,
"Id" : ObjectId("53c7b54f12727064000000a9")
},
],
.
.
Iam尝试实现一个函数,其中我的PHP脚本接收一个Ids数组,用于玩家想要声明的优秀奖励,处理奖励项目,并将新处理的条目插入GameObjects:[]
如果我错了,请纠正我,但由于$elemMatch
只返回1个匹配的元素,我只是执行
db.find(
{ _id: ObjectId("..playerId...")},
{_id: 0, "OutstandingRewards": 1, "GameObjects":1}
)
在PHP端匹配他们想要声明的所有奖励,并为他们分配对象。
为了强制执行分配操作的事务属性,我已经提出了2个解决方案,并且想知道哪个性能更好(或者如果我认为这完全错误,那么设计条款或其他方式)。
在PHP数组中本地存储整个GameObject[]
(可能会增长大,100个可能是1,000个对象),将任何声明的奖励附加到此数组并同时重写整个GameObject[]
{ {1}}对象被删除:
OutstandingRewards[]
Iam担心这可能会占用大量内存,并且在db.update({ _id: ObjectId("..playerId...")},
{
$pull: {"OutstandingRewards": {"Id": {$in:$PHPClaimIdsArray}}},
$set: {"GameObjects":$PHPNewRewardsArray}
},
);
很大时会产生大量冗余写入。
对于用户想要声明的每个奖励,请执行“查找和修改”操作。拨打:
GameObjects[]
我担心在这种情况下,PHP-MongoDb服务器通信和多次写入开销将会扼杀我。
提前致谢!
答案 0 :(得分:2)
我可以为您提供解决方案3 ,在这种情况下,您将使用$pull
从“OustandingRewards”列表中删除所有项目,然后使用{{3}使用$push
修饰符,以便立即将这些项添加到“GameObjects”数组中。
此处的表单意味着$pull
实际上将“查询”对象作为其参数。这里最明显的是$each
运算符,其中包含要删除的每个“Id”值。
因此,如果您的“选择列表”恰好恰好是当前的“Oustanding”项目(已经“反序列化”到PHP数据),那么您可以执行以下操作:
# $input = array from JSON
# [
# { "Type" : 0,"Id" : ObjectId("53c7b54f12727064000000a0")},
# { "Type" : 1, Id" : ObjectId("53c7b54f12727064000000a2")}
# ],
function getIds($v)
{
return $v["Id"];
}
$ids = array_map("getIds",$input);
$collection->findAndModify(
array(
'_id' => $playerId,
'OutstandingRewards.Id' => array(
'$in' => $ids
),
'GameObjects' => array(
'$nin' => $input
)
),
array(
'$pull' => array(
'OutstandingRewards' => array (
'Id' => array( '$in' => $ids )
)
),
'$push' => array(
'GameObjects' => (
'$each' => $input,
'$slice' => 10000
)
)
),
null,
array( 'new' => true )
)
我以特别“偏执”的形式构建了声明中的“查询”部分。这意味着它分别在“OustandingRewards”和“GameObjects”上使用$in
和$in
操作,因此如果数组实际上不包含(或不包含)对象,则查询实际上不会匹配在$input
数组中显示。
$pull
的参数是使用$in
删除与该查询匹配的项目的“查询”。
在$push
操作中,$each
修饰符实际上采用数组作为其参数。这意味着该数组中所有元素都应用于单一的“push”操作,而不是采用具有多个更新的循环。
$nin
修饰符在2.6之前的MongoDB版本中是必需的。目的是“限制”与此类操作一起使用时阵列可能包含的元素数量。通常这被认为是一个好主意,但在最新版本中,您不需要它。这里只设置一个你希望大于数组中可能元素的数字,这样就不会带走任何东西。
当然,在最新版本中,您可以修改“偏执狂”的级别并在此处理,因为$slice
也可以使用$each
修饰符。就像JavaScript现在解释PHP语法一样:
var input; // the same JavaScript object
var ids = input.map(function(x) { return x.Id } );
db.collection.findAndModify({
"query": {
"_id": player,
"OutstandingRewards.Id": { "$in": ids }
},
"update": {
"$pull": {
"OutstandingRewards": {
"Id": { "$in": ids }
}
},
"$addToSet": {
"GameObjects": { "$each": input }
}
},
"new": true
})
正如最后一点,即使你可以在某种程度上,将数组无限期地扩展或特别大的数字通常也不是一个好主意。当然,永远不会为文档打破16MB,并且在性能ID受到显着影响之前,确实应该保持在500以下。实际处理可能很大的“列表”,例如这本身就是另一个问题。
但这至少为您提供了一种方法,可以从一个数组中删除和插入多个项目,并在单个原子操作中转移到同一文档中的另一个数组。