[英]Upsert Array Elements matching criteria in a MongoDB document?
As per How do I update Array Elements matching criteria in a MongoDB document? 根据如何在MongoDB文档中更新匹配条件的数组元素?
I want to upsert the array elements, so if one doesnt match then insert it, otherwise update it. 我想要插入数组元素,所以如果一个不匹配然后插入它,否则更新它。
I tried the answer on that question, and it works fine IF the array element already exists. 我尝试了这个问题的答案,如果数组元素已经存在,它可以正常工作。 If the element doesnt exist then it creates a child of "$" under the array field. 如果元素不存在,那么它会在数组字段下创建一个子元素“$”。
My Mongo structure is as follows: 我的Mongo结构如下:
Widget (collection)
--Name
--Properties (array)
--Name
--Value
My application gets a Widget Name and a list of Properties from a WebService call. 我的应用程序从WebService调用获取Widget Name和Properties列表。 I wish to iterate the provided Properties and update the value in the MongoDB if the Name already exists, OR insert a new Property to the Properties array if it doesnt. 我希望迭代提供的属性并在Name已经存在时更新MongoDB中的值,或者如果不存在则将新属性插入Properties数组。
What you require is not possible using a single update without some app-side logic. 如果没有某些应用程序端逻辑,则无法使用单个更新。 Note that upsert as a feature is not relevant for this specific problem unless you want to automatically create new Widget documents if none exist with the provided name. 请注意,upsert作为一项功能与此特定问题无关,除非您想要自动创建新的Widget文档(如果不存在所提供的名称)。
The problem you're running into is that there is no functionality that allows you to do two different updates depending on the existence of an array element. 您遇到的问题是没有任何功能允许您根据数组元素的存在执行两个不同的更新。 Your only two options are : 您唯一的两个选择是:
You cannot atomically upsert array elements. 您不能以原子方式插入数组元素。 But if you can restructure your document to use an object instead of an array, then this is atomically possible. 但是,如果您可以重新构建文档以使用对象而不是数组,那么这在原子上是可行的。 Using your notation, the structure would be 使用您的符号,结构将是
Widget (collection)
--Name
--Properties
--Property1
--Property2
.
:
An example document would be 一个示例文档将是
{
name: 'widget-1',
properties: {
'property-1': 100,
'property-2': 200
}
}
To upsert an item named 'property-3' with value 300, you'd do 要将值为300的名为“property-3”的项目竖起,您可以这样做
db.widgets.update(
{ name: 'widget-1' },
{ $set: { 'properties.property-3': 300 } }
)
One drawback is that querying for field names (using the $exists
query operator) requires scanning. 一个缺点是查询字段名称(使用$exists
查询运算符)需要扫描。 You can get around this by adding an extra array field that stores the property names. 您可以通过添加存储属性名称的额外数组字段来解决此问题。 This field can be indexed and queried normally. 可以正常索引和查询该字段。 Upserts become Upsert成为
db.widgets.update(
{ name: 'widget-1' },
{
$set: { 'properties.property-3': 300 },
$addToSet: { propertyNames: 'property-3' }
}
)
(Keep in mind $addToSet
is O(n).) When removing a property, you have to pull it from the array as well. (请记住$addToSet
是O(n)。)删除属性时,您也必须从数组中提取它。
db.widgets.update(
{ name: 'widget-1' },
{
$unset: { 'properties.property-3': true },
$pull: { propertyNames: 'property-3' }
}
)
At least in php it works for me, try to adopt to your language. 至少在php中它对我有用,尝试采用你的语言。 You need your collection look like: 你需要你的收藏看起来像:
{Name: x, Properties: {name1:value1, name2:value2, name3:value3}}
foreach ($Properties as $name => $value)
{
$propertiesArray["Properties.$name"] = $value;
}
$criteria = array('Name' => $name);
$operation = array('$set' => $propertiesArray);
$options = array('upsert' => true);
$collection = $this->_dbh->selectCollection('Widget');
$collection->update($criteria, $operation, $options);
It inserts new names if not exist and updates those which already there 如果不存在则插入新名称并更新已存在的名称
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.