【问题标题】:How to update an array element in a deeply nested mongodb structure using C# (Trying to use positional operator to update the document))如何使用 C# 更新深度嵌套的 mongodb 结构中的数组元素(尝试使用位置运算符更新文档)
【发布时间】:2019-11-04 15:58:05
【问题描述】:

我正在尝试访问深度嵌套的 mongodb 结构,并希望使用 c# 更新和读取该结构。我想更新 UUT 数组中的 UUT 对象以及 UUT 历史数组。

我正在尝试更新 UUTId 的值:TS220,时钟周期:2000。

我知道一个覆盖完整文档的选项,但我不知道如何访问 UUTId 对象并为时钟周期插入一个值。

请使用 c# MongoDB.Driver 回答 提前致谢。

MongoClient mongoClient = new MongoClient("mongodb://server.net:27017");
IMongoDatabase db = mongoClient.GetDatabase("enduranceTest");
IMongoCollection<ServerTreeVm> collectionEndurance = db.GetCollection<ServerTreeVm>("EnduranceTests");


var filterVM = Builders<ServerTreeVm>.Filter;
            var enduranceTestFound = filterVM.And(
                Builders<ServerTreeVm>.Filter.Where(x => x.Countries.Any(y =>
                    y.Cities.Any(z => z.EnduranceTests.Any(a => a.EnduranceTestName == "Endurance 1")))),
                Builders<ServerTreeVm>.Filter.Where(x => x.Countries.Any(y =>
                    y.Cities.Any(z => z.EnduranceTests.Any(a => a.EnduranceInfo.Any(b=>b.Title== "Taktverteiler_1"))))),
                Builders<ServerTreeVm>.Filter.Where(x => x.Countries.Any(y =>
                    y.Cities.Any(z => z.EnduranceTests.Any(a => a.EnduranceInfo.Any(b => b.Channels.Any(c=>c.Uuts.Any(d=>d.UutId == "TS220"))))))));

            var enduranceTest = collectionEndurance.Find(enduranceTestFound).SingleOrDefault();

            //update with positional operator
            var update = Builders<ServerTreeVm>.Update;

            UUT uutObj = new UUT
            {
              Status = "Running",
              UutId = "TS220",
              ClockingCycles = 2000,
              Offset = 100,
              StartCycles = 0,
              Timestamp = DateTime.Now,
              UUTHistory = null
            };

            var enduranceUpdate = update.Set("Countries.$.Cities.$.EnduranceTest.$.ClockDistributor.$.Channels.$.UUTs.$", uutObj);
            collectionEndurance.UpdateOne(enduranceTestFound, enduranceUpdate);

所以我在 C# ServerTreeVM 模型中从 mongodb 获取整个文档,然后我尝试使用位置运算符更新文档。但它没有更新文档。我不知道我在这里做错了什么。有什么提示或不同的方法吗?

{
  "_id": {
    "$oid": "5d5ff0be962f936368c28b1f"
  },
  "Countries": [
    {
      "CountryName": "Country1",
      "Cities": [
        {
          "CityName": "City1",
          "EnduranceTests": [
            {
              "EnduranceTestName": "endurance1",
              "ClockDistributor": [
                {
                  "IP": "192.168.2.6",
                  "Channels": [
                    {
                      "UUTs": [
                        {
                          "UUTId": "TS220",
                          "ClockingCycles": {
                            "$numberInt": "0"
                          },
                          "Status": "Steht",
                          "StartCycles": {
                            "$numberInt": "0"
                          },
                          "Offset": {
                            "$numberInt": "0"
                          },
                          "Timestamp": {
                            "$date": {
                              "$numberLong": "946681200000"
                            }
                          },
                          "UUTHistory": [
                            {
                              "ClockingCycles": {
                                "$numberInt": "0"
                              },
                              "Offset": {
                                "$numberInt": "0"
                              },
                              "Status": "",
                              "StartCycles": {
                                "$numberInt": "0"
                              },
                              "Timestamp": {
                                "$date": {
                                  "$numberLong": "946681200000"
                                }
                              }
                            }
                          ]
                        },
                        {
                          "UUTId": "TS221",
                          "ClockingCycles": {
                            "$numberInt": "0"
                          },
                          "Status": "Steht",
                          "StartCycles": {
                            "$numberInt": "0"
                          },
                          "Offset": {
                            "$numberInt": "0"
                          },
                          "Timestamp": {
                            "$date": {
                              "$numberLong": "946681200000"
                            }
                          },
                          "UUTHistory": [
                            {
                              "ClockingCycles": {
                                "$numberInt": "0"
                              },
                              "Offset": {
                                "$numberInt": "0"
                              },
                              "Status": "",
                              "StartCycles": {
                                "$numberInt": "0"
                              },
                              "Timestamp": {
                                "$date": {
                                  "$numberLong": "946681200000"
                                }
                              }
                            }
                          ]
                        }
                      ],
                      "ChannelCycles": {
                        "$numberInt": "0"
                      },
                      "NodeName": ""
                    },
                    {
                      "ChannelCycles": {
                        "$numberInt": "0"
                      },
                      "UUTs": [
                        {
                          "UUTId": "TS230",
                          "ClockingCycles": {
                            "$numberInt": "0"
                          },
                          "Status": "Steht",
                          "StartCycles": {
                            "$numberInt": "0"
                          },
                          "Offset": {
                            "$numberInt": "0"
                          },
                          "Timestamp": {
                            "$date": {
                              "$numberLong": "946681200000"
                            }
                          },
                          "UUTHistory": [
                            {
                              "ClockingCycles": {
                                "$numberInt": "0"
                              },
                              "Offset": {
                                "$numberInt": "0"
                              },
                              "Status": "",
                              "StartCycles": {
                                "$numberInt": "0"
                              },
                              "Timestamp": {
                                "$date": {
                                  "$numberLong": "946681200000"
                                }
                              }
                            }
                          ]
                        }
                      ],
                      "NodeName": ""
                    },
                    {
                      "UUTs": [
                        {
                          "UUTId": "TS240",
                          "ClockingCycles": {
                            "$numberInt": "0"
                          },
                          "Status": "Steht",
                          "StartCycles": {
                            "$numberInt": "0"
                          },
                          "Offset": {
                            "$numberInt": "0"
                          },
                          "Timestamp": {
                            "$date": {
                              "$numberLong": "946681200000"
                            }
                          },
                          "UUTHistory": [
                            {
                              "ClockingCycles": {
                                "$numberInt": "0"
                              },
                              "Offset": {
                                "$numberInt": "0"
                              },
                              "Status": "",
                              "StartCycles": {
                                "$numberInt": "0"
                              },
                              "Timestamp": {
                                "$date": {
                                  "$numberLong": "946681200000"
                                }
                              }
                            }
                          ]
                        }
                      ],
                      "ChannelCycles": {
                        "$numberInt": "0"
                      },
                      "NodeName": ""
                    },
                    {
                      "ChannelCycles": {
                        "$numberInt": "0"
                      },
                      "UUTs": [
                        {
                          "UUTId": "TS250",
                          "ClockingCycles": {
                            "$numberInt": "0"
                          },
                          "Status": "Steht",
                          "StartCycles": {
                            "$numberInt": "0"
                          },
                          "Offset": {
                            "$numberInt": "0"
                          },
                          "Timestamp": {
                            "$date": {
                              "$numberLong": "946681200000"
                            }
                          },
                          "UUTHistory": [
                            {
                              "ClockingCycles": {
                                "$numberInt": "0"
                              },
                              "Offset": {
                                "$numberInt": "0"
                              },
                              "Status": "",
                              "StartCycles": {
                                "$numberInt": "0"
                              },
                              "Timestamp": {
                                "$date": {
                                  "$numberLong": "946681200000"
                                }
                              }
                            }
                          ]
                        }
                      ],
                      "NodeName": ""
                    }
                  ],
                  "ConnectionType": "Opcua",
                  "Title": "Taktverteiler_1",
                  "Link": "http://192.168.0.6:8080/tv_webvisu.htm",
                  "PropertyNames": [
                    "P-Line",
                    "Modus",
                    "Status",
                    "Freq [Hz]",
                    "Verwendung"
                  ],
                  "Nodes": [
                    "|var|CPX-CEC-S1-V3.Application.PersistentVars.ar_str_p1_p2",
                    "|var|CPX-CEC-S1-V3.Application.OPC_UA_Variablen_Dashboard.ar_str_Normal_VE_OPC_UA",
                    "|var|CPX-CEC-S1-V3.Application.OPC_UA_Variablen_Dashboard.ar_str_Status_OPC_UA",
                    "|var|CPX-CEC-S1-V3.Application.PersistentVars.ar_r_Frequenz_Kanal_X",
                    "|var|CPX-CEC-S1-V3.Application.OPC_UA_Variablen_Dashboard.ar_str_Verwendung"
                  ],
                  "StartStopNodes": [
                    "|var|CPX-CEC-C1-V3.Application.GVL.b_Button_Stimulation_Starten_aktive_Kanaele",
                    "|var|CPX-CEC-C1-V3.Application.GVL.b_alle_Stopp"
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
[BsonIgnoreExtraElements]
    public class ServerTreeVm
    {
        [BsonElement("Countries")]
        public List<Country> Countries { get; set; }

        public string ActiveEnduranceTest { get; set; }

        public string ActiveDistributor { get; set; }

    }

    [BsonIgnoreExtraElements]
    public class Country
    {
        [BsonElement("Cities")]
        public List<City> Cities { get; set; }
        [BsonElement("CountryName")]
        public string CountryName { get; set; }
    }

    [BsonIgnoreExtraElements]
    public class City
    {
        [BsonElement("EnduranceTests")]
        public List<Endurance> EnduranceTests { get; set; }
        [BsonElement("CityName")]
        public string CityName { get; set; }
    }

    [BsonIgnoreExtraElements]
    public class Endurance
    {
        [BsonElement("ClockDistributor")]
        public List<EnduranceInformation> EnduranceInfo { get; set; }
        [BsonElement("EnduranceTestName")]
        public string EnduranceTestName { get; set; }
    }


    [BsonIgnoreExtraElements]
    public class UUT
    {
        [BsonElement("UUTId")]
        public string UutId { get; set; }
        [BsonElement("ClockingCycles")]
        public double ClockingCycles { get; set; }
        [BsonElement("Status")]
        public string Status { get; set; }
        [BsonElement("StartCycles")]
        public double StartCycles { get; set; }
        [BsonElement("Offset")]
        public double Offset { get; set; }
        [BsonElement("Timestamp")]
        public DateTime Timestamp { get; set; }
        [BsonElement("UUTHistory")]
        public List<UUTHistory> UUTHistory { get; set; }
    }


    [BsonIgnoreExtraElements]
    public class UUTHistory
    {
        [BsonElement("ClockingCycles")]
        public double ClockingCycles { get; set; }
        [BsonElement("Status")]
        public string Status { get; set; }
        [BsonElement("StartCycles")]
        public double StartCycles { get; set; }
        [BsonElement("Offset")]
        public double Offset { get; set; }
        [BsonElement("Timestamp")]
        public DateTime Timestamp { get; set; }
    }


    [BsonIgnoreExtraElements]
    public class ChannelObjects
    {
        [BsonElement("UUTs")]
        public List<UUT> Uuts { get; set; }
        [BsonElement("ChannelCycles")]
        public double ChannelCycles { get; set; }
        [BsonElement("NodeName")]
        public string NodeName { get; set; }

    }


    [BsonIgnoreExtraElements]
    public class EnduranceInformation
    {
        [BsonElement("IP")]
        public string Ip { get; set; }

        [BsonElement("Title")]
        public string Title { get; set; }

        [BsonElement("Link")]
        public string Link { get; set; }

        [BsonElement("PropertyNames")]
        public List<string> PropertyNames { get; set; }

        [BsonElement("Nodes")]
        public List<string> Nodes { get; set; }

        [BsonElement("Channels")]
        public List<ChannelObjects> Channels { get; set; }

        [BsonElement("ConnectionType")]
        public String ConnectionType { get; set; }

        [BsonElement("StartStopNodes")]
        public List<string> StartStopNodeNames { get; set; }
    }

【问题讨论】:

  • MongoDB C# 驱动程序以 BsonDocument() 类型返回数据。您有自定义模型。您尚未指定用于在 MongoDB 驱动程序和自定义模型之间进行序列化/反序列化的框架。您的 DAL 使用什么?
  • 顺便说一句,批量更新文档(即替换整个文档以更新单个字段)成本高且性能不佳。
  • 我使用来自 Nuget 包的官方 C# Mongodb 驱动程序。链接:github.com/mongodb/mongo-csharp-driver

标签: c# mongodb


【解决方案1】:

这并不能真正解决您的真正问题,即如何使用 C# mongo 驱动程序,但这可能会有所帮助。我把它放在这里而不是上面的 cmets 是因为我想说明一些代码概念并且需要格式化选项...

请允许我详细说明一些要点...

您的代码具有以下 lambda 查询条件...

(a => a.EnduranceInfo.Any(b=>b.Title== "Taktverteiler_1"))

...但是您的示例文档没有此字段。这将导致找不到要更新的文档。同样,由于EnduranceInfo 不存在以下 lambda 查询表达式 ....

(a => a.EnduranceInfo.Any(b => b.Channels.Any(c=>c.Uuts.Any(d=>d.UutId == "TS220"))))

... 也将无法找到匹配的文档。

我经常将 MongoDB 与 RDBMS 系统进行比较。在 Mongo 中构建标准的“find()”操作时,第一个条件是查询。这是一个可怕的名字,因为它不是查询,而是“查找匹配的文档”子句。如果您创建 MongoDB find() 命令并仅提供第一个参数(假设找到匹配项),您将收到整个文档。这意味着第一个参数相当于传统 RDBMS 系统中的“WHERE”子句。尚未建立“SELECT”子句。

所以,你的问题更有针对性。您不仅要查找要更新的特定文档,还希望更新找到的文档的特定部分。查询的查找部分查找文档。然后必须定义查询的其他部分以标识要更新文档的哪个部分。您的代码尝试通过两行代码定位文档的特定部分...

var enduranceUpdate = update.Set("Countries.$.Cities.$.EnduranceTest.$.ClockDistributor.$.Channels.$.UUTs.$", uutObj);
collectionEndurance.UpdateOne(enduranceTestFound, enduranceUpdate);

我认为您的想法是正确的,但从语法上讲,find 参数enduranceTestFound 在前面的讨论中不会有任何找到的文档。选项 enduranceUpdate 格式错误。 '$' 的使用表示您要更新数组元素的第一次出现。请参阅https://docs.mongodb.com/manual/reference/operator/update/positional/ 上有关“$”的 MongoDB 文档。你会看到他们特别声明...

位置 $ 运算符充当第一个元素的占位符 匹配查询文档,并且数组字段必须显示为 查询文档的一部分。

为了了解我的代码需要做什么,我通常会首先使用可用的最基本工具来识别查询。对于通常是 SQL 窗口的 RDBMS 系统。对于 MongoDB 那是 mongo shell。我的理论是,如果我可以创建一个在 mongo shell 中正确执行的查询,那么我只需将它转换为我的代码框架,在你的情况下是 C# 驱动程序特定的语法。通过使用更新命令的 arrayFilters 选项,我可以创建一个非常有针对性的查询。此查询通过“查找”部分找到正确的文档,然后识别 $set 命令以针对要更新的特定字段,然后使用 arrayFilters 选项我可以使用占位符变量来定义我希望更新的文档的特定部分。这是我的例子:

db.enduranceTest.update(
    { "Countries.Cities.EnduranceTests.EnduranceTestName": "endurance1" }, 
    { $set: {
        "Countries.$[].Cities.$[].EnduranceTests.$[].ClockDistributor.$[].Channels.$[].UUTs.$[myplaceholder].ClockingCycles" : 2000,
        "Countries.$[].Cities.$[].EnduranceTests.$[].ClockDistributor.$[].Channels.$[].UUTs.$[myplaceholder].Status" : "Running",
        "Countries.$[].Cities.$[].EnduranceTests.$[].ClockDistributor.$[].Channels.$[].UUTs.$[myplaceholder].StartCycles" : 0,
        "Countries.$[].Cities.$[].EnduranceTests.$[].ClockDistributor.$[].Channels.$[].UUTs.$[myplaceholder].Offset" : 2000,
        "Countries.$[].Cities.$[].EnduranceTests.$[].ClockDistributor.$[].Channels.$[].UUTs.$[myplaceholder].Timestamp" : new Date(),
        "Countries.$[].Cities.$[].EnduranceTests.$[].ClockDistributor.$[].Channels.$[].UUTs.$[myplaceholder].UUTHistory" : null,
      } 
    },
    {
      arrayFilters: [
        { "myplaceholder.UUTId" : "TS220" }
      ]
    }
)

这里有很多好东西。注意myplaceholder 变量名以及它在arrayFilter 区域和$set 运算符中的引用方式?注意$[] 表示任何数组。您有这么多嵌套数组,我需要在 $set 中为每个字段添加 5 个 $[] 运算符。请注意,我正在逐个字段设置。我本可以替换整个$[myplaceholder] - 我相信这是您最初的方法。请注意,我的“查找”命令仅限于“Countries.Cities.EnduranceTests.EnduranceTestName”,EnduranceInfo 不存在,因此我不包含它。

无论如何,我期待您的回复。这是一个有趣的练习,因此我对 arrayFilters 有了更深入的了解。我怀疑 C# 驱动程序有一个用于 arrayFilters 的构造,并且理解如何应用它们仍然是一项任务。希望通过查看原始 MQL,您可以找到转换为 C# 特定代码的选项。

【讨论】:

    【解决方案2】:
    • 如果您仔细查看 JSON 结构 (a =&gt; a.EnduranceInfo.Any(b=&gt;b.Title== "Taktverteiler_1")) "title":"Taktverteiler_1" 确实存在于结构中。
    • 关于您的解决方案,我觉得很好。但我安装的 mongodb 服务器不支持数组过滤器。因此,我无法判断该解决方案是否有效。当我使用没有数组过滤器的解决方案时,我会得到 ​​p>

      var enduranceUpdate = update.Set( "Countries.$[].Cities.$[].EnduranceTest.$[].ClockDistributor.$[].Channels.$[].UUTs.$[0].ClockingCycles", uutObj.ClockingCycles);

      Mongodb异常不能使用部分(Countries of countries.$[].Cities.$[].EnduranceTest.$[].ClockDistributor.$[].Channels.$[].UUTs.$[0].ClockingCycles ) 来遍历元素。

    • 我可以让我的解决方案之一工作,但这不是最好的解决方案,因为我需要在动态更新每个数组之前知道它的索引。

      ` UUT uutObj = new UUT
                      {
                        Status = "laüft",
                        UutId = "TS220",
                        ClockingCycles = 2000,
                        Offset = 100,
                        StartCycles = 0,
                        Timestamp = DateTime.Now,
                        UUTHistory = null
                      };
      
          var enduranceUpdate = update.Set("Countries.0.Cities.0.EnduranceTest.0.ClockDistributor.0.Channels.0.UUTs.$", uutObj);`
      

      var enduranceTestFound = filterVM.And( Builders<ServerTreeVm>.Filter.Where(x => x.Countries.Any(y => y.Cities.Any(z => z.EnduranceTests.Any(a => a.EnduranceInfo.Any(b => b.Channels.Any(c=>c.Uuts.Any(d=>d.UutId == "TS220")))))))); UpdateResult res = collectionEndurance.UpdateOne(enduranceTestFound, enduranceUpdate);

    【讨论】:

      猜你喜欢
      • 2014-12-27
      • 2013-08-12
      • 1970-01-01
      • 2019-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-23
      相关资源
      最近更新 更多