Arrays 是否在MongoDB文档中插入数组元素匹配条件?

Arrays 是否在MongoDB文档中插入数组元素匹配条件?,arrays,mongodb,upsert,Arrays,Mongodb,Upsert,依照 我想向上插入数组元素,因此如果其中一个不匹配,则插入它,否则更新它 我尝试了这个问题的答案,如果数组元素已经存在,它就可以正常工作。如果元素不存在,则在数组字段下创建“$”的子元素 我的Mongo结构如下: Widget (collection) --Name --Properties (array) --Name --Value 我的应用程序从WebService调用中获取一个小部件名称和一个属性列表。如果名称已经存在,我希望迭代提供的属性并更新MongoDB中的值,如果名称不

依照

我想向上插入数组元素,因此如果其中一个不匹配,则插入它,否则更新它

我尝试了这个问题的答案,如果数组元素已经存在,它就可以正常工作。如果元素不存在,则在数组字段下创建“$”的子元素

我的Mongo结构如下:

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

我的应用程序从WebService调用中获取一个小部件名称和一个属性列表。如果名称已经存在,我希望迭代提供的属性并更新MongoDB中的值,如果名称不存在,则在属性数组中插入新属性。

如果没有一些应用程序端逻辑,使用单个更新是不可能实现您所需的。请注意,upsert作为一项功能与此特定问题无关,除非您希望在没有提供名称的小部件文档时自动创建新的小部件文档

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

  • 找到项目,确定相关属性的存在,使用新属性或更改属性编译适当的更新并执行。这带来了一个重要的缺点,即这不是一个并发安全的方法。换句话说,如果两个web服务同时尝试此操作,则其中一个可能会覆盖彼此的更改
  • 使小部件属性成为顶级文档,而不是嵌入文档。允许您使用upserts做您想做的事情。明显的缺点是,就模式设计而言,这不是一个很好的选择。例如,如果您获取一个小部件,您不会自动获取所有属性

  • 至少在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);
    

    如果不存在新名称,它将插入新名称,并更新那些已经存在的名称。

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

    Widget (collection)
      --Name
      --Properties
        --Property1
        --Property2
            .
            :
    
    一个例子是

    {
        name: 'widget-1',
        properties: {
            'property-1': 100,
            'property-2': 200
        }
    }
    
    要向上插入名为“property-3”且值为300的项,请执行以下操作

    db.widgets.update(
        { name: 'widget-1' }, 
        { $set: { 'properties.property-3': 300 } }
    )
    
    一个缺点是查询字段名(使用
    $exists
    查询运算符)需要扫描。您可以通过添加一个额外的存储属性名的数组字段来解决这个问题。此字段可以正常索引和查询。上升的部分变成

    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' }
        }
    )
    

    我也得出了这个结论。我每个小部件大约有3500个属性,可能超过100000个小部件。将属性放在自己的集合中是明智的设计吗?如果您可以重新构造文档,则“升级”是可能的。看我的答案。这是正确的,但你没有真正解释为什么这样做。询问者需要重新构造文档以使用对象而不是数组。更多细节请参见我的答案。(另外,使用upsert标志将upsert整个文档,但问题是关于数组元素的。)但这也有其缺点。例如,不能为元素中的字段编制索引。此外,还有各种工具和类型系统不太支持这个概念。例如,如果您想将此类型映射到GraphQL,那就太倒霉了,因为它不支持objects.True中的动态键。如果您绝对需要原子upsert并使用GraphQL公开集合,则需要将对象结构转换为数组(例如,使用自定义解析器使用Apollo)。根据我的理解,从mongodb 4.2开始,您还可以使用管道进行原子更新,以向数组中upsert。