C#/ Mongo-根据父项和子项条件将数据选择到嵌套数组的对象中

时间:2019-04-30 11:11:25

标签: c# mongodb

我有一个C#模型,该模型具有嵌套列表数组,并且已作为文档实现到Mongo中。

我想将数据返回到嵌套数组的结构中,但是我想基于父级上的字段和数组内的字段选择数据。

我尝试了ElemMatch和Project的数十种组合,但是现在我完全迷失了,甚至不确定是否可以使用驱动程序来完成我要尝试的操作。

public class Models
{
    public ObjectId _id { get; set; }
    public string ModelType{ get; set; }
    public List<ModelList> ModelList { get; set; } = new List<ModelList>();
}
public class ModelList
{
    public string ModelHashKey { get; set; }
    public string ModelName { get; set; }
    public string ModelAttribute { get; set; }
}

上面显示的数据模型。

我想从ModelList中选择所有记录,其中: Models.ModelType =“ Player” AND ModelList.ModelAttribute =“ Male”

即使这是Models的元素,我也想将数据返回到对象列表ModelList中。

您将如何解决此问题,还是只从父级中选择数据然后在C#中循环?

非常感谢,

戴夫

2 个答案:

答案 0 :(得分:1)

因此,让我们从绘图板开始,并通过mongo控制台获取一些数据:

db.models.insertMany([
  {
     _id : 1,
     ModelType: "Player",
     ModelList: [
       { ModelHashKey: "1", ModelName: "1", ModelAttribute: "Male" },
       { ModelHashKey: "2", ModelName: "2", ModelAttribute: "Male" },
       { ModelHashKey: "3", ModelName: "3", ModelAttribute: "Female" }
     ]
 },
 {
     _id : 2,
     ModelType: "NotPlayer",
     ModelList: [
       { ModelHashKey: "4", ModelName: "4", ModelAttribute: "Male" },
       { ModelHashKey: "5", ModelName: "5", ModelAttribute: "Male" },
       { ModelHashKey: "6", ModelName: "6", ModelAttribute: "Female" }
     ]
 }
]);

从这个问题开始,我假设您只想选择_id为1的文档,还要将ModelLiet的列表过滤为2个Male(1、2)。

因此,让我们首先整理一下文档,这可以通过简单的查找来完成:

db.models.find({"ModelType": "Player", "ModelList.ModelAttribute": "Male"}).pretty()
  

请注意,我们可以添加索引{"ModelType" : 1, "ModelList.ModelAttribute": 1 }来支持该查询。

但是,如果执行此查找,我们将取回整个文档:

{
        "_id" : 1,
        "ModelType" : "Player",
        "ModelList" : [
                {
                        "ModelHashKey" : "1",
                        "ModelName" : "1",
                        "ModelAttribute" : "Male"
                },
                {
                        "ModelHashKey" : "2",
                        "ModelName" : "2",
                        "ModelAttribute" : "Male"
                },
                {
                        "ModelHashKey" : "3",
                        "ModelName" : "3",
                        "ModelAttribute" : "Female"
                }
        ]
}

因此,在这里我们想将其转换为聚合查询并使用过滤器投影数据。

db.models.aggregate([
  { $match: {"ModelType": "Player", "ModelList.ModelAttribute": "Male" } },
  { $addFields: { "ModelList" : {
            $filter: {
               input: "$ModelList",
               as: "item",
               cond: { $eq: [ "$$item.ModelAttribute", "Male" ] }
           }
        }
    }
  } 
]);

如果执行上述操作,我们将获得预期的结果(请在此处尝试-https://mongoplayground.net/p/j6bP9TE6aTD):

{
        "_id" : 1,
        "ModelType" : "Player",
        "ModelList" : [
                {
                        "ModelHashKey" : "1",
                        "ModelName" : "1",
                        "ModelAttribute" : "Male"
                },
                {
                        "ModelHashKey" : "2",
                        "ModelName" : "2",
                        "ModelAttribute" : "Male"
                }
        ]
}

因此,我们使用MongoDB驱动程序将其隐藏到C#中,它们的语法非常相似,只是语法有所不同:

public class Models
{
    public int _id { get; set; }
    public string ModelType { get; set; }
    public List<ModelList> ModelList { get; set; } = new List<ModelList>();
}

public class ModelList
{
    public string ModelHashKey { get; set; }
    public string ModelName { get; set; }
    public string ModelAttribute { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        var client = new MongoClient();
        var db = client.GetDatabase("test");
        var collection = db.GetCollection<Models>("models");

        var models = collection.Aggregate()
            .Match(Builders<Models>.Filter.Eq(x => x.ModelType, "Player") & Builders<Models>.Filter.ElemMatch(x => x.ModelList, Builders<ModelList>.Filter.Eq(x => x.ModelAttribute, "Male")))
            .AppendStage<Models>(BsonDocument.Parse(@"{ $addFields: { ""ModelList"" : { $filter: { input: ""$ModelList"", as: ""item"", cond: { $eq: [""$$item.ModelAttribute"", ""Male""] } } } } }"))
            .ToList();

        foreach (var model in models)
        {
            foreach (var item in model.ModelList)
            {
                Console.WriteLine(item.ToJson());
            }
        }
    }
}

请注意,我们正在使用AppendStage<>作为C#驱动程序,目前还不支持该聚合阶段。

现在,如果我们运行此C#代码,我们将得到以下结果:

{ "ModelHashKey" : "1", "ModelName" : "1", "ModelAttribute" : "Male" }
{ "ModelHashKey" : "2", "ModelName" : "2", "ModelAttribute" : "Male" }

答案 1 :(得分:1)

如果您不介意将ModelList项存储在自己的集合中,这是我使用MongoDB.Entities的解决方案,它不使用任何魔术字符串。

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

public class Models : Entity
{
    public string ModelType { get; set; }
    public Many<ModelList> ModlList { get; set; }

    public Models() => this.InitOneToMany(() => ModlList);
}

public class ModelList : Entity
{
    public string ModelHashKey { get; set; }
    public string ModelName { get; set; }
    public string ModelAttribute { get; set; }
    public One<Models> Parent { get; set; }
}

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

        var parent = new Models { ModelType = "Player" };
        parent.Save();

        var ml1 = new ModelList
        {
            ModelAttribute = "Male",
            ModelName = "i am one",
            ModelHashKey = "secret",
            Parent = parent.ToReference()
        };
        ml1.Save();

        var ml2 = new ModelList
        {
            ModelAttribute = "Female",
            ModelName = "i am two",
            ModelHashKey = "secret",
            Parent = parent.ToReference()
        };
        ml2.Save();

        parent.ModlList.Add(ml1);
        parent.ModlList.Add(ml2);

        var result = (from m in DB.Collection<Models>()
                      where m.ModelType == "Player"
                      join l in DB.Collection<ModelList>() on m.ID equals l.Parent.ID into lists
                      from ml in lists
                      select ml).Where(l => l.ModelAttribute == "Male");

        var modellists = result.ToArray();

        Console.Write(modellists.First().ModelName);
        Console.ReadKey();
    }
}

产生的mongodb聚合:

aggregate([{ 
"$match" : 
    { "ModelType" : "Player" } }, 
    { "$lookup" : {"from" : "ModelLists", 
                   "localField" : "_id", 
                   "foreignField" : "Parent.ID", "as" : "lists" } }, 
                    { "$unwind" : "$lists" }, 
                    { "$project" : { "lists" : "$lists", "_id" : 0 } }, 
                    { "$match" : { "lists.ModelAttribute" : "Male" } }])