Php MongoDB:原子更新&;数组比较
编辑:解决方案3(我正在使用的) 此原子更新可确保在我希望删除所有未完成ID并将相关已处理奖励添加到Php MongoDB:原子更新&;数组比较,php,mongodb,performance,mongodb-query,Php,Mongodb,Performance,Mongodb Query,编辑:解决方案3(我正在使用的) 此原子更新可确保在我希望删除所有未完成ID并将相关已处理奖励添加到游戏对象[]时,我希望声明的所有未完成ID仍存在于未完成奖励[]中。此外,使用$addToSet操作符可以确保在GameObjects[]中不会插入重复的条目 基于Neil Lunn以下答案的解决方案 原始Qn: 为具有可申请奖励的游戏编写DB,数据结构如下: . . "OutstandingRewards" : [ { "Type"
游戏对象[]
时,我希望声明的所有未完成ID仍存在于未完成奖励[]
中。此外,使用$addToSet
操作符可以确保在GameObjects[]
中不会插入重复的条目
基于Neil Lunn以下答案的解决方案
原始Qn:
为具有可申请奖励的游戏编写DB,数据结构如下:
.
.
"OutstandingRewards" : [
{
"Type" : 0,
"Id" : ObjectId("53c7b54f12727064000000a0")
},
{
"Type" : 1,
"Id" : ObjectId("53c7b54f12727064000000a2")
},
],
"GameObjects":[
{
"Type" : 1,
"Id" : ObjectId("53c7b54f12727064000000a5")
},
{
"Type" : 3,
"Id" : ObjectId("53c7b54f12727064000000a9")
},
],
.
.
我正在尝试实现一个函数,在这个函数中,我的PHP脚本为玩家想要领取的未完成奖励接收一系列ID,处理奖励项目,并将新处理的条目插入到GameObjects:[]
如果我错了,请纠正我,因为$elemMatch
只返回1个匹配的元素,所以我只执行
db.find(
{ _id: ObjectId("..playerId...")},
{_id: 0, "OutstandingRewards": 1, "GameObjects":1}
)
在PHP端匹配他们想要获得的所有奖励,并为他们分配对象
为了加强分配操作的事务属性,我提出了两种解决方案,并想知道哪一种性能更好(或者如果我在设计或其他方面完全错了)。
解决方案1:
将整个GameObject[]
(可能会变大,100个或1000个对象)本地存储在一个PHP数组中,将任何声称的奖励附加到此数组中,并重写整个GameObject[]
,同时删除OutstandingRewards[]
对象:
db.update({ _id: ObjectId("..playerId...")},
{
$pull: {"OutstandingRewards": {"Id": {$in:$PHPClaimIdsArray}}},
$set: {"GameObjects":$PHPNewRewardsArray}
},
);
我担心这可能会占用大量内存,并且在GameObjects[]
较大时会产生大量冗余写入
解决方案2:
对于用户想要申请的每个奖励,执行一次“findAndModify”呼叫:
db.findAndModify(
query: {
_id: ObjectId("..playerId..."),
"OutstandingRewards": {$elemMatch: {"Id": $rewardId}}
},
update: {
$pull: {"OutstandingRewards": {"Id": $rewardId}},
$push: {"GameObjects":$rewardObject}
}
)
在这种情况下,我担心PHP-MongoDb服务器通信和多重写入开销会让我丧命
提前谢谢 我可以为您提供解决方案3,在这种情况下,您将使用从“OustandingRewards”列表中删除所有项目,然后使用修改器,以便立即将这些项目添加到“GameObjects”数组中 这里的形式意味着
$pull
实际上将“query”对象作为其参数。这里要应用的最明显的是带有每个要删除的“Id”值的运算符
因此,如果您的“选择列表”恰好是当前的两个“独立”项(已经“反序列化”到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”进行的操作,因此如果数组实际上不包含(或不包含)在$input
数组中显示的对象,则查询实际上不会匹配
$pull
的参数是使用$in
删除与该查询匹配的项的“查询”
在$push
操作中,$each
修饰符实际上将数组作为其参数。因此,这意味着该数组中的所有元素都应用于单个“推”操作,而不是使用带有多个更新的循环
在2.6之前的MongoDB版本中,该修饰符是必需的。其目的是“限制”数组在与此类操作一起使用时可能包含的元素数。一般来说,这被认为是一个好主意,但在最新版本中,您不需要它。在这里,它只是被设置为一个你期望大于数组中可能的元素的数字,这样就不会有任何东西被拿走
当然,在最新版本中,您可以在此处修改“偏执狂”的级别和处理方式,也可以使用$each
修饰符。这一次就像现在的JavaScript一样,PHP语法被解释为:
var输入;//相同的JavaScript对象
var Id=input.map(函数(x){return x.Id});
db.collection.findAndModify({
“查询”:{
“_id”:玩家,
“OutstandingRewards.Id”:{“$in”:ids}
},
“更新”:{
“$pull”:{
“杰出奖励”:{
“Id”:{“$in”:ids}
}
},
“$addToSet”:{
“游戏对象”:{“$each”:输入}
}
},
“新”:真的
})
最后一点要注意的是,尽管在某种程度上可以,但将阵列无限期地扩展或扩展到特别大的数量通常不是一个好主意。当然,文档永远不会超过16MB,而且在性能id明显受到影响之前,应该将其保持在500以下。然而,像这样潜在的大型“列表”的实际处理本身确实是另一个问题
但这至少为您提供了一种方法,可以在单个原子操作中从一个数组中删除和插入多个项,并将它们传输到同一文档中的另一个数组。Ahhh。。。是的,我意识到如果
杰出奖励
上的mongoid在转换为游戏对象时保持持久,那么“偏执查询”就可以工作了!以前我在创建游戏对象时使用了newmongoid()
,这会使“偏执查询”的$nin
部分始终返回true。谢谢你的回答!从逻辑上讲,我们实际上只需要游戏对象上的$nin
,对吗?OutstandingRewards[]
上的$in
返回true,只要至少有1个元素匹配,对吗?例如,Id{$in:[X,Y,Z]}
将返回true,如果Id:X
,Id:Y
,Id:Z
中的任何一个存在,那么as$nin
将返回false,如果它们中的任何一个存在,对吗?好的,我刚刚搞乱了
# $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 )
)