【问题标题】:Editing an array element nested inside an array element mongo编辑嵌套在数组元素 mongo 中的数组元素
【发布时间】:2017-01-07 21:54:06
【问题描述】:

我有一个基于 mongo 的应用程序,它显示报告。 在每个报告中,用户可以发布显示给所有其他用户的 cmets,以及回复现有的 cmets。 cmets 有两个“级别”,顶级 cmets 没有“父母”,回复“父母”。评论存储在一个数组“discussions”中,每个评论都有另一个名为“replies”的数组。

db.reports.findOne()
{
    "_id" : "L154654258",
    "timestamp" : ISODate("2016-03-17T01:15:00Z"),
    "title" : "Server Offline",
    "content" : "A server has become offline due to reason X112",
    "discussions" : [
        {
            "id" : ObjectId("57beb8068d9da75ed44e0ebc"),
            "user_id" : "david",
            "timestamp" : ISODate("2016-03-17T01:15:00Z"),
            "comment" : "Is this the first time that it happens?",
            "last_edit" : null,
            "replies" : [
                {
                    "id" : ObjectId("57beb8068d9da75ed44e0ebd"),
                    "user_id" : "gene",
                    "timestamp" : ISODate("2016-03-17T01:20:00Z"),
                    "comment" : "I have never seen anything like that before",
                    "last_edit" : null
                },
                {
                    "id" : ObjectId("57c480b7ee568ddc634fd977"),
                    "user_id" : "david",
                    "timestamp" : ISODate("2016-03-20T01:20:00Z"),
                    "comment" : "Thanks!",
                    "last_edit" : null
                }
        }
    ]
}

到目前为止,使用此方案,我能够使用位置 $ 运算符处理向每个评论(父母和回复)插入新项目,以及编辑父母的内容(如果用户选择编辑他们的帖子) . 我的问题是更新回复。我试过这个(我认为它不起作用,只是想测试一下):

db.reports.findOne({'_id': 'L154654258', 'discussions': {'$elemMatch': {'id': ObjectId("57beb8068d9da75ed44e0ebc"), 'replies': {'$elemMatch': {'id': ObjectId("57beb8068d9da75ed44e0ebd")}}}}},
{'$set' : {'discussions.$.replies.$.comment': "This is a test!", 'discussions.$.replies.$.last_edit': 'Fantastic'}}
)

用 findOne() 测试这个查询似乎表明查询的 find 部分有效。但是,如何更新数组的数组元素? 我假设两次使用 $ 运算符是行不通的,但是有没有办法做到这一点,或者我需要使用 Javascript 吗? 谢谢!

【问题讨论】:

  • 是的,这就是数组嵌套过多的问题。你不能有效地与他们合作。在我们的应用程序中,我们只允许一级嵌入式数组,正是出于这个原因。
  • 谢谢!我希望使用 javascript 来执行它(类似于下面的 Sharabanee 的回答)。将回复嵌套在“父”cmets 中是有意义的,因为我们希望限制每页显示的“父”cmets 的数量(无论这些 cmets 有多少回复)...

标签: mongodb


【解决方案1】:

你不能用 $ 操作符来做。它总是会更新第一个元素。您必须找到该文档,在您的服务器中对其进行更改,然后将其保存回 mongo。

【讨论】:

    【解决方案2】:

    试试下面的方法:-

    var findQuery = {'_id': 'L154654258', 'discussions.replies.id': ObjectId("57beb8068d9da75ed44e0ebd")};
    
    db.restaurants.find(
    findQuery).toArray(
    function(err, result)
    {
        if(err)
        {
             console.log(err);
        }
        else if(result && result.length)
        {
          var index,found = false;
         for(var j in result[0].discussions){
          for(var i in result[0].discussions[j].replies)
          {
             if(results[0].discussions[j].replies[i].id == ObjectId("57beb8068d9da75ed44e0ebd"))
             {
                found = true;
                index = i;
                break;
            }  
         }
       }
    
       if(found)
       {
            var x = 'discussions.$.replies.'+index+'.last_edit';
    
            var     updateObject = {$set : {}};    
            updateObject['$set'][x] = 'Fantastic';
    
            db.restaurants.update(findQuery, updateObject);
        }
      });
    }
    

    【讨论】:

    • 这更符合我正在寻找的内容。但是,由于某种原因,它不起作用。它执行(带有其他更改,例如将餐厅更改为报告),但它只是将查找结果显示为数组。 function(err, result) 中没有代码实际运行(即使它只是 print("hello"!);) 任何帮助将不胜感激!
    • 它进入了第一个和第二个 for 循环?
    • 我知道这有点复杂。但截至目前,我认为您必须使用这种方式来更新所需的字段。
    • 我理解答案中的代码,由于某种原因它只是不执行。这是我正在寻找的解决方案类型!它只用数组括号 [ ] 显示整个记录一次。如前所述,只是将 print("HELLO") 放在 toArray 函数中似乎并没有改变任何东西。好像没有执行这个函数的内容……再次感谢!
    • 尝试更改查找查询。 [] 这意味着没有结果匹配findQuery。您可以将{'_id': 'L154654258'} 作为 findQuery 并检查吗?有没有记录?
    【解决方案3】:

    基于 Sharabanee 的代码,下面是对我有用的代码:

    var findQuery = {'_id': 'L154654258', 'discussions.replies.id': ObjectId("57beb8068d9da75ed44e0ebd")};
    var result = db.reports.find(findQuery).toArray();
    var index,found = false;
    for(var j in result[0].discussions) {
        for(var i in result[0].discussions[j].replies) {
             if(result[0].discussions[j].replies[i].id.equals(ObjectId("57beb8068d9da75ed44e0ebd"))) {
                found = true;
                index = i;
                break;
            }  
        }
    }
    if(found) {
        var x = 'discussions.$.replies.'+index+'.comment';
    
        var  updateObject = {$set : {}};    
        updateObject['$set'][x] = 'Fantastic';
    
        db.reports.update(findQuery, updateObject);
    }
    

    【讨论】:

    • 完全不是原子的并且容易出现竞争条件。这可能不是您关心的问题。 JFYI。
    • 竞态条件是什么意思?什么时候会发生这种情况?
    • 考虑这种情况:客户端 C1 读取讨论 D 并开始搜索要更新的回复。它在索引 3 处找到一个。但在它有机会执行更新之前,客户端 C2 修改了相同的讨论 D 以使索引无效(例如,删除第一个回复)。 C1 没有注意到这一变化,继续设置索引为 3 的回复字段,但这将是一个错误的回复。
    【解决方案4】:

    我知道这篇文章很旧,但我只是在寻找自己如何在 Mongo 中更新数组中的元素。

    借助从 v3.6 开始的update,可以使用 arrayFilters 纯粹在 Mongo 中进行操作,而无需知道数组中的位置。

    还有另一个答案详细说明了如何使用这个发现here

    我希望这可以帮助那些也在寻找如何在不使用 Mongo 之外的代码引入竞争条件的情况下解决此问题的人。

    【讨论】:

      猜你喜欢
      • 2017-01-05
      • 2015-04-02
      • 1970-01-01
      • 2022-08-03
      • 2020-05-30
      • 2018-10-06
      • 1970-01-01
      • 2013-11-22
      • 2011-04-04
      相关资源
      最近更新 更多