MongoDB C#使用“定义”构建器检索文档中数组中的所有匹配元素

时间:2019-05-20 16:40:39

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

我有一个结构类似嵌套子文档

的文档
var cookie

我想在具有圆形图的本文档中检索fooArray中的所有匹配元素。

这是我尝试过的

String?

这就是我得到的

{  
   "_id":ObjectId("50419077c2e6a1e18a489a0f"),
   "user":"Jone Doe",
   "fooArray":[  
      {  
         "plot":"circle",
         "color":"yellow",
      },
      {  
         "plot":"circle",
         "color":"red",
      },
      {  
         "plot":"square",
         "color":"green",
      }
   ]
}

但是它只给我第一个匹配的元素,而不是所有具有等于圆的图的元素

var filter = FilterBuilder.filter.Eq(doc => doc.User, User);
var projection = ProjectionBuilder
                .Exclude(doc => doc.Id)
                .Exclude(doc => doc.User)
                .Include(doc => doc.FooArray)
                .ElemMatch(x => x.FooArray, y => y.Plot == "circle");

var definition = new OperationDefinitions<ShapeDocument> { Filter = filter };
            return await Performer.Perform(definition, async (def, collection) =>
            {
                var findResult = collection.Find(def.Filter).Project(projection);

                var result = await findResult.SingleOrDefaultAsync();
            });

我确实阅读了提及的mongodb文档

“ $ elemMatch运算符将查询结果中字段的内容限制为仅包含与$ elemMatch条件匹配的第一个元素。”

不太确定如何实现!

3 个答案:

答案 0 :(得分:1)

该问题并未完全描述用例,因此我根据一些假设为您提供了一些潜在的选项供您探索,尤其是它们取决于LINQ是否可用以及将单个文档作为目标。一段时间(而且您可能不想要超出实际需要的代码):

1)您所拥有的产品有所不同。使用带有投影和LINQ表达式的标准find

var projection = Builders<ShapeDocument>.Projection
    .Expression(x => x.fooArray.Where(y => y.plot == "circle"));

var items1 = collection
    .Find(x => x.user == "Jone Doe")
    .Project(projection)
    .ToList();

2)使用聚合管道(您可以使用与上面相同的投影)

var pipeline = collection
    .Aggregate()
    .Match(x => x.user == "Jone Doe")
    .Project(i => new
            {
                x = i.fooArray.Where(x => x.plot == "circle")
            });

var items2 = pipeline.SingleOrDefault();

3)将文档连同所有数组元素一起拉回,然后使用LINQ在本地进行过滤。从好的方面来说,这是少量的可读代码,但是,确实会在过滤之前将整个文档带回来。根据您的确切使用情况,这可能是可以接受的。

var items3 = collection.AsQueryable()
    .SingleOrDefault(x => x.user == "Jone Doe")
    .fooArray.Where(x => x.plot == "circle");

如果不是真的要使用LINQ,则有一个示例here,该示例显示了如何将投影转换为我们的LINQ。完全未经测试,但可能类似于:

var filter = new BsonDocument {
 {"input", "$items"},
 {"as", "item" },
 {"cond", new BsonDocument {
     // Fill in the condition values
     { "", new BsonArray { "", xxx } } }
   }
 };

var project = new BsonDocument {
 { "items", new BsonDocument { { "$filter", filter} } }
};

var pipeline = collection.Aggregate().Project(project);

答案 1 :(得分:0)

这是一个使用MongoDB.Entities的简单解决方案,它只是c#驱动程序的包装库。

using MongoDB.Driver.Linq;
using MongoDB.Entities;
using System.Linq;

namespace StackOverflow
{
    public class User : Entity
    {
        public string Name { get; set; }
        public Foo[] Foos { get; set; }
    }

    public class Foo
    {
        public string Plot { get; set; }
        public string Color { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            new DB("test");

            var user = new User
            {
                Name = "Jone Doe",
                Foos = new[]
                {
                    new Foo{ Plot = "circle", Color="yellow"},
                    new Foo{ Plot = "circle", Color="red"},
                    new Foo{ Plot = "square", Color="green"},
                }
            };

            user.Save();

            var circularFoos = DB.Collection<User>()
                                 .Where(u => u.Name == "Jone Doe")
                                 .SelectMany(u => u.Foos)
                                 .Where(f=>f.Plot=="circle").ToArray();
        }
    }
}

这是它生成的汇总查询:

  {
    "$match": {
      "Name": "Jone Doe"
    }
  },
  {
    "$unwind": "$Foos"
  },
  {
    "$project": {
      "Foos": "$Foos",
      "_id": 0
    }
  },
  {
    "$match": {
      "Foos.Plot": "circle"
    }
  }

答案 2 :(得分:0)

我想出了一种简洁的方法

var filter = FilterBuilder.filter.Eq(doc => doc.User, User);
var definition = new OperationDefinitions<ShapeDocument> { Filter = filter };
return await Performer.Perform(definition, async (def, collection) =>
{
var findResult = collection.Find(def.Filter).Project(doc => doc.fooArray.Where(x => x.Plot == "Circle"));
var result = await findResult.SingleOrDefaultAsync();
}