在 MongoDB 文档中更新插入匹配条件的数组元素?

发布于 2024-12-26 18:54:38 字数 486 浏览 0 评论 0原文

根据 如何更新数组元素匹配 MongoDB 文档中的条件?

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

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

我的 Mongo 结构如下:

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

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

As per How do I update Array Elements matching criteria in a MongoDB document?

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:

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

My application gets a Widget Name and a list of Properties from a WebService call. 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

所谓喜欢 2025-01-02 18:54:38

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

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

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

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.

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.
  2. Make widget properties top level documents rather than embedded. Allows you to use upserts to do what you want. 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.
何必那么矫情 2025-01-02 18:54:38

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

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

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

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. 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

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.

db.widgets.update(
    { name: 'widget-1' }, 
    { 
        $unset: { 'properties.property-3': true },
        $pull: { propertyNames: 'property-3' }
    }
)
巴黎夜雨 2025-01-02 18:54:38

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

它插入新名称(如果不存在)并更新那些已经存在的名称

At least in php it works for me, try to adopt to your language.
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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文