简体   繁体   English

在MongoDB文档中匹配符合条件的数组元素?

[英]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 : 您唯一的两个选择是:

  1. Find the item, determine existence of relevant propertie(s), compile an appropriate update with your new or changes properties and execute it. 找到该项目,确定相关属性的存在,使用您的新属性或更改属性编译适当的更新并执行它。 This comes with the important downside that this is not a concurrency safe method. 这带来了重要的缺点,即这不是一种并发安全方法。 In other words, if two web services attempt this at the same one could overwrite eachother's changes. 换句话说,如果两个Web服务尝试同一个,则可能会覆盖彼此的更改。
  2. Make widget properties top level documents rather than embedded. 使小部件属性成为顶级文档而不是嵌入式。 Allows you to use upserts to do what you want. 允许您使用upserts来执行您想要的操作。 Obvious downside is that that isn't a very nice option in terms of schema design. 显而易见的缺点是,在架构设计方面,这不是一个非常好的选择。 You wouldn't automatically get all properties if you fetch a widget, for example. 例如,如果您获取小部件,则不会自动获取所有属性。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM