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

时间:2012-01-15 16:40:26

标签: arrays mongodb upsert

根据How do I update Array Elements matching criteria in a MongoDB document?

我想要插入数组元素,所以如果不匹配则插入它,否则更新它。

我在这个问题上尝试了答案,如果数组元素已经存在,它可以正常工作。如果元素不存在,那么它会在数组字段下创建一个子元素“

。”

我的Mongo结构如下:

Widget (collection)
--Name
--Properties (array)
  --Name
  --Value

我的应用程序从WebService调用中获取Widget Name和Properties列表。我希望迭代提供的属性并在Name已经存在的情况下更新MongoDB中的值,或者如果不存在则将新属性插入到Properties数组中。

3 个答案:

答案 0 :(得分:10)

如果没有某些应用程序端逻辑,则无法使用单个更新。请注意,upsert作为一项功能与此特定问题无关,除非您想要自动创建新的Widget文档(如果不存在所提供的名称)。

您遇到的问题是没有任何功能允许您根据数组元素的存在执行两个不同的更新。您唯一的两个选择是:

  1. 找到该项目,确定相关属性的存在,使用您的新属性或更改属性编译适当的更新并执行它。这带来了重要的缺点,即这不是一种并发安全方法。换句话说,如果两个Web服务在同一个网站上尝试此操作,则可能会覆盖彼此的更改。
  2. 使小部件属性成为顶级文档而不是嵌入式文档。允许您使用upserts来执行您想要的操作。显而易见的缺点是,在架构设计方面,这不是一个非常好的选择。例如,如果您获取小部件,则不会自动获取所有属性。

答案 1 :(得分:4)

您不能以原子方式插入数组元素。但是,如果您可以重新构建文档以使用对象而不是数组,那么这在原子上是可行的。使用您的符号,结构将是

Widget (collection)
  --Name
  --Properties
    --Property1
    --Property2
        .
        :

示例文档将是

{
    name: 'widget-1',
    properties: {
        'property-1': 100,
        'property-2': 200
    }
}

要为值为300的名为“property-3”的项目竖起,您需要

db.widgets.update(
    { name: 'widget-1' }, 
    { $set: { 'properties.property-3': 300 } }
)

一个缺点是查询字段名称(使用$exists查询运算符)需要扫描。您可以通过添加存储属性名称的额外数组字段来解决此问题。可以正常索引和查询该字段。 Upserts成为

db.widgets.update(
    { name: 'widget-1' }, 
    { 
        $set: { 'properties.property-3': 300 },
        $addToSet: { propertyNames: 'property-3' }
    }
)

(请记住$addToSet是O(n)。)删除属性时,您也必须从数组中提取它。

db.widgets.update(
    { name: 'widget-1' }, 
    { 
        $unset: { 'properties.property-3': true },
        $pull: { propertyNames: 'property-3' }
    }
)

答案 2 :(得分:1)

至少在php中它对我有用,尝试采用你的语言。 你需要你的收藏品如下:

        {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);

如果不存在则插入新名称并更新已存在的名称