【问题标题】:How can I 'AND' multiple $elemMatch clauses with C# and MongoDB?如何使用 C# 和 MongoDB 'AND' 多个 $elemMatch 子句?
【发布时间】:2011-06-07 14:43:04
【问题描述】:

我正在使用 10Gen 认可的 c# 驱动程序,用于 mongoDB 的 c# 应用程序和数据浏览,我正在使用 Mongovue。

以下是两个示例文档架构:

{
  "_id": {
    "$oid": "4ded270ab29e220de8935c7b"
  },
  "Relationships": [
    {
      "RelationshipType": "Person",
      "Attributes": {        
        "FirstName": "Travis",
        "LastName": "Stafford"
      }
    },
    {
      "RelationshipType": "Student",
      "Attributes": {
        "GradMonth": "",
        "GradYear": "",
        "Institution": "Test1",
      }
    },
    {
      "RelationshipType": "Staff",
      "Attributes": {
        "Department": "LIS",
        "OfficeNumber": "12",
        "Institution": "Test2",
      }
    }
  ]
},    

{
  "_id": {
    "$oid": "747ecc1dc1a79abf6f37fe8a"
  },
  "Relationships": [
    {
      "RelationshipType": "Person",
      "Attributes": {        
        "FirstName": "John",
        "LastName": "Doe"
      }
    },
    {
      "RelationshipType": "Staff",
      "Attributes": {
        "Department": "Dining",
        "OfficeNumber": "1",
        "Institution": "Test2",
      }
    }
  ]
}

我需要一个查询来确保满足两个 $elemMatch 条件,以便我可以匹配第一个文档,但不能匹配第二个文档。以下查询在 Mongovue 中有效。

{
  'Relationships': { $all: [
        {$elemMatch: {'RelationshipType':'Student', 'Attributes.Institution': 'Test1'}},
        {$elemMatch: {'RelationshipType':'Staff', 'Attributes.Institution': 'Test2'}}
     ]}
}

如何在我的 c# 代码中执行相同的查询?

【问题讨论】:

    标签: c# mongodb mongodb-.net-driver


    【解决方案1】:

    没有办法使用 c# 驱动程序构建上述查询(至少在 1.0 版中)。

    但是您可以构建另一个更清晰的查询,它会返回相同的结果:

    { "Relationships" : 
              { "$elemMatch" : 
                  { "RelationshipType" : "Test", 
                    "Attributes.Institution" : { "$all" : ["Location1", "Location2"] } 
                  } 
              } 
    }
    

    还有来自 c# 的相同查询:

    Query.ElemMatch("Relationships", 
        Query.And(
            Query.EQ("RelationshipType", "Test"),
                Query.All("Attributes.Institution", "Location1", "Location2")));
    

    【讨论】:

    • 感谢您的快速回复。不幸的是,您建议的查询不会返回相同的结果。当我刚开始使用 MongoDB 时,我的文档设计很可能是这里的问题。 Attributes 是一个子文档,Attributes.Institution 不是数组。无论如何将查询字符串直接发送到 Find 函数?
    • @TravisStafford:是的,您可以从字符串构建查询。看看我的答案:stackoverflow.com/questions/6120629/…。我试过了,但没有成功;(。
    • 谢谢你,这是一个巨大的帮助。阅读您在上一条评论中指向我的帖子,我能够构建一个快速类来构建我需要的查询字符串。它并不完美,但可以完成工作。
    • 我已按要求发布了我的结果。很高兴能就我所采取的方法获得一些意见。
    【解决方案2】:

    一个简单的解决方案是将多个 IMongoQuery(s) 串在一起,然后用一个 Query 将它们连接起来。最后:

    List<IMongoQuery> build = new List<IMongoQuery>();
    build.Add(Query.ElemMatch("Relationships", Query.EQ("RelationshipType", "Person")));
    
    var searchQuery =  String.Format("/.*{0}.*/", "sta");
    build.Add(Query.ElemMatch("Relationships", Query.Or(Query.EQ("Attributes.FirstName", new BsonRegularExpression(searchQuery)), Query.EQ("Attributes.LastName", new BsonRegularExpression(searchQuery)))));
    
    var _main = Query.And(build.ToArray());
    
    var DB = MongoDatabase.Create("UrlToMongoDB");
    DB.GetCollection<ObjectToQuery>("nameOfCollectionInMongoDB").FindAs<ObjectToQuery>(_main).ToList();
    

    `

    【讨论】:

      【解决方案3】:

      我通过构建一组允许生成以下查询的类解决了当前的问题:

      {  'Relationships': 
          { 
              $all: [
               {$elemMatch: {'RelationshipType':'Student', 'Attributes.Institution': 'Test1'}},            
               {$elemMatch: {'RelationshipType':'Staff',   'Attributes.Institution': 'Test2'}}     
              ]
          }
      }
      

      以下是类定义:

      class MongoQueryAll
          {
              public string Name { get; set; }
              public List<MongoQueryElement> QueryElements { get; set; }
      
              public MongoQueryAll(string name)
             {
                 Name = name;
                 QueryElements = new List<MongoQueryElement>();
             }
      
             public override string ToString()
             {
                 string qelems = "";
                 foreach (var qe in QueryElements)
                    qelems = qelems + qe + ",";
      
                 string query = String.Format(@"{{ ""{0}"" : {{ $all : [ {1} ] }} }}", this.Name, qelems); 
      
                 return query;
             }
        }
      
      class MongoQueryElement
      {
          public List<MongoQueryPredicate> QueryPredicates { get; set; }
      
          public MongoQueryElement()
          {
              QueryPredicates = new List<MongoQueryPredicate>();
          }
      
          public override string ToString()
          {
              string predicates = "";
              foreach (var qp in QueryPredicates)
              {
                  predicates = predicates + qp.ToString() + ",";
              }
      
              return String.Format(@"{{ ""$elemMatch"" : {{ {0} }} }}", predicates);
          }
      }
      
      class MongoQueryPredicate
      {        
          public string Name { get; set; }
          public object Value { get; set; }
      
          public MongoQueryPredicate(string name, object value)
          {
              Name = name;
              Value = value;
          }
      
          public override string ToString()
          {
              if (this.Value is int)
                  return String.Format(@" ""{0}"" : {1} ", this.Name, this.Value);
      
              return String.Format(@" ""{0}"" : ""{1}"" ", this.Name, this.Value);
          }
      }
      

      助手搜索类:

      public class IdentityAttributeSearch
      {
          public string Name { get; set; }
          public object Datum { get; set; }
          public string RelationshipType { get; set; }
      }
      

      示例用法:

       public List<IIdentity> FindIdentities(List<IdentityAttributeSearch> searchAttributes)
       {
              var server = MongoServer.Create("mongodb://localhost/");
              var db = server.GetDatabase("IdentityManager");
              var collection = db.GetCollection<MongoIdentity>("Identities");
      
              MongoQueryAll qAll = new MongoQueryAll("Relationships");
      
              foreach (var search in searchAttributes)
              {
                  MongoQueryElement qE = new MongoQueryElement();
                  qE.QueryPredicates.Add(new MongoQueryPredicate("RelationshipType", search.RelationshipType));
                  qE.QueryPredicates.Add(new MongoQueryPredicate("Attributes." + search.Name, search.Datum));
                  qAll.QueryElements.Add(qE);
              }
      
              BsonDocument doc = MongoDB.Bson.Serialization
                      .BsonSerializer.Deserialize<BsonDocument>(qAll.ToString());
      
              var identities = collection.Find(new QueryComplete(doc)).ToList();
      
              return identities;
          }
      

      我确信有一个更好的方法,但这目前可行,并且似乎足够灵活,可以满足我的需求。欢迎所有建议。

      这可能是一个单独的问题,但由于某种原因,在 100,000 个文档集上进行此搜索最多可能需要 24 秒。我尝试添加各种索引但无济于事;在这方面的任何指针都会很棒。

      【讨论】:

        猜你喜欢
        • 2014-09-20
        • 1970-01-01
        • 2019-03-12
        • 2013-09-03
        • 2017-09-26
        • 2016-08-13
        • 1970-01-01
        • 1970-01-01
        • 2011-01-16
        相关资源
        最近更新 更多