简体   繁体   English

如何使用MongoDb将一个分组应用于另一个分组的结果?

[英]How can apply a group by to a result of another group by using MongoDb?

Let's suppose I have the following POCO's representing documents in Mongo: 假设我在Mongo中具有以下POCO代表文档:

public class A
{
    [BsonId, BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
    public string Name { get; set; }
    public string LastName { get; set; }
    public bool Locked { get; set; }
}
public class B
{
    [BsonId, BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
    public string Name { get; set; }
}
public class C
{
    [BsonId, BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
    public string Name { get; set; }
}
public class AB
{
    [BsonId, BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
    [BsonRepresentation(BsonType.ObjectId)]
    public string AId { get; set; }
    [BsonRepresentation(BsonType.ObjectId)]
    public string BId { get; set; }
}
public class AC
{
    [BsonId, BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
    [BsonRepresentation(BsonType.ObjectId)]
    public string AId { get; set; }
    [BsonRepresentation(BsonType.ObjectId)]
    public string CId { get; set; }
}

The relationships are A many-to-many B and A many-to-many C . 关系是A many-to-many BA many-to-many C My goal is to query all A documents that aren't locked and include all B 's and C 's related to the A document. 我的目标是查询所有未锁定的A文档,包括与A文档有关的所有BC For that I've tried a lot of attempts but I'll post the most (I think) completed one: 为此,我尝试了很多尝试,但我将发布最多(我认为)完成的尝试:

var q = from a in A_Col.AsQueryable()
     where !a.Locked
     join ab in AB_Col.AsQueryable() on a.Id equals ab.AId
     join b in B_Col.AsQueryable() on ab.BId equals b.Id into Bs
     group new { a.Id, a.Name, a.LastName, Bs = Bs.DefaultIfEmpty() }
     by new { a.Id, a.Name, a.LastName } into ABs
     from abs in ABs.DefaultIfEmpty()
     join ac in AC_Col.AsQueryable() on abs.Id equals ac.AId
     join c in C_Col.AsQueryable() on ac.CId equals c.Id into Cs
     group new { abs.Id, abs.Name, abs.LastName, abs.Bs, Cs = Cs.DefaultIfEmpty() }
     by new { abs.Id, abs.Name, abs.LastName };

When I try to execute the query I get the following error: 当我尝试执行查询时,出现以下错误:

The method GroupBy is not supported in the expression tree: aggregate([]).Where(a => Not(a.Locked)).Join(aggregate([]), a => a.Id, ab => ab.AId, (a, ab) => new <>f__AnonymousType0 2(a = a, ab = ab)).GroupJoin(aggregate([]), <>h__TransparentIdentifier0 => <>h__TransparentIdentifier0.ab.BId, b => b.Id, (<>h__TransparentIdentifier0, Bs) => new <>f__AnonymousType1 2(<>h__TransparentIdentifier0 = <>h__TransparentIdentifier0, Bs = Bs)).GroupBy(<>h__TransparentIdentifier1 => new <>f__AnonymousType3 3(Id = <>h__TransparentIdentifier1.<>h__TransparentIdentifier0.a.Id, Name = <>h__TransparentIdentifier1.<>h__TransparentIdentifier0.a.Name, LastName = <>h__TransparentIdentifier1.<>h__TransparentIdentifier0.a.LastName), <>h__TransparentIdentifier1 => new <>f__AnonymousType2 4(Id = <>h__TransparentIdentifier1.<>h__TransparentIdentifier0.a.Id, Name = <>h__TransparentIdentifier1.<>h__TransparentIdentifier0.a.Name, LastName = <>h__TransparentIdentifier1.<>h__TransparentIdentifier0.a.LastName, Bs = <>h_ 表达式树不支持GroupBy方法:aggregate([])。Where(a => Not(a.Locked))。Join(aggregate([]),a => a.Id,ab => ab。 AId,(a,ab)=>新<> f__AnonymousType0 2(a = a, ab = ab)).GroupJoin(aggregate([]), <>h__TransparentIdentifier0 => <>h__TransparentIdentifier0.ab.BId, b => b.Id, (<>h__TransparentIdentifier0, Bs) => new <>f__AnonymousType1 2(<> h__TransparentIdentifier0 = <> h__TransparentIdentifier0,Bs = Bs))。GroupBy(<> h__TransparentIdentifier1 => new <> f__AnonymousType3 3(Id = <>h__TransparentIdentifier1.<>h__TransparentIdentifier0.a.Id, Name = <>h__TransparentIdentifier1.<>h__TransparentIdentifier0.a.Name, LastName = <>h__TransparentIdentifier1.<>h__TransparentIdentifier0.a.LastName), <>h__TransparentIdentifier1 => new <>f__AnonymousType2 = <> h__TransparentIdentifier1。<> h__TransparentIdentifier0.a.Id,名称= <> h__TransparentIdentifier1。<> h__TransparentIdentifier0.a.Name,LastName = <> h__TransparentIdentifier1。<> h__TransparentIdentifier0.a.LastName,Bs = <> h_ _TransparentIdentifier1.Bs.DefaultIfEmpty())) _TransparentIdentifier1.Bs.DefaultIfEmpty()))

I also noted that the resolved GroupBy is coming from System.Linq namespace (not 100% sure) and not from MongoDB.Driver.Linq namespace. 我还注意到,已解析的GroupBy来自System.Linq命名空间(不确定100%),而不是来自MongoDB.Driver.Linq命名空间。 After a while modifying here and there I tried to do so in the following way: 经过一段时间的修改后,我尝试通过以下方式进行修改:

var q = A_Col.AsQueryable().Where(a => !a.Locked)
    .Join(
       AB_Col.AsQueryable(),
       a => a.Id, 
       ab => ab.AId,
       (a, ab) => new
       {
          A = new { a.Id, a.Name, a.LastName },
          AB = new { ab.AId, ab.BId }
       })
    .Join(
       B_Col.AsQueryable(), 
       a_doc => a_doc.AB.BId,
       b => b.Id, 
       (a, b) => new { a.A.Id, a.A.Name, a.A.LastName, B = new { b.Name, b.Id } })
    .GroupBy(doc => new { doc.Id, doc.Name, doc.LastName })
    .Join(
        AC_Col.AsQueryable(),
        g => g.Key.Id,
        ac => ac.AId,
        (g, ac) => new
        {
           g.Key.Id,
           g.Key.Name,
           g.Key.LastName,
           Bs = g.Select(x => x.B),
           AC = new { ac.AId, ac.CId }
        })
    .Join(
        C_Col.AsQueryable(),
        gac => gac.AC.CId,
        c => c.Id,
        (gac, c) => new { gac.Id, gac.Name, gac.LastName, gac.Bs, C = new { c.Name, c.Id } })
    .GroupBy(doc => new { doc.Id, doc.Name, doc.LastName, doc.Bs })
    .Select(g => new { g.Key.Id, g.Key.Name, g.Key.LastName, g.Key.Bs, Cs = g.Select(x => x.C) });

And now the error is 现在的错误是

$project or $group does not support {document}. $ project或$ group不支持{document}。

I've seen this one before but in the above code I can't see why it's happening. 我之前已经看过这个,但是在上面的代码中我看不到它为什么发生。 Can you help me out? 你能帮我吗? It's so frustrating I can't figure this out! 实在令人沮丧,我无法解决!

The next data inserted into a test DB: 接下来的数据插入到测试数据库中:

var mongoClient = new MongoClient("mongodb://localhost");
var database = mongoClient.GetDatabase("TestDb");

var a1 = new A { Name = "A1->Name", LastName = "A1->LastName", Locked = false };
var a2 = new A { Name = "A2->Name", LastName = "A2->LastName", Locked = true };

var A_Col = database.GetCollection<A>("A_COL");
A_Col.InsertMany(new[] { a1, a2 });

var b1 = new B { Name = "B1->Name" };
var b2 = new B { Name = "B2->Name" };
var b3 = new B { Name = "B3->Name" };

var B_Col = database.GetCollection<B>("B_COL");
B_Col.InsertMany(new[] { b1, b2, b3 });

var c1 = new C { Name = "C1->Name" };
var c2 = new C { Name = "C2->Name" };
var c3 = new C { Name = "C3->Name" };

var C_Col = database.GetCollection<C>("C_COL");
C_Col.InsertMany(new[] { c1, c2, c3 });

var ab1 = new AB { AId = a1.Id, BId = b1.Id };
var ab2 = new AB { AId = a1.Id, BId = b2.Id };

var AB_Col = database.GetCollection<AB>("AB_COL");
AB_Col.InsertMany(new[] { ab1, ab2 });

var ac1 = new AC { AId = a1.Id, CId = c1.Id };
var ac2 = new AC { AId = a1.Id, CId = c2.Id };
var ac3 = new AC { AId = a1.Id, CId = c3.Id };

var AC_Col = database.GetCollection<AC>("AC_COL");
AC_Col.InsertMany(new[] { ac1, ac2, ac3 });

Should give the following results: 应给出以下结果:

* a1
    -Bs
      b1
      b2
    -Cs
      c1
      c2
      c3 

Thanks in advance! 提前致谢!

Environment: .net core 2.1, MongoDb Version: 4.0.3, Driver Version: 2.7.1 环境:.net core 2.1,MongoDb版本:4.0.3,驱动程序版本:2.7.1

Update: 更新:

If the below doesn't help, please post IQueryable example of what MongoDB driver is generating. 如果以下内容无济于事,请发布MongoDB驱动程序生成的IQueryable示例。 You can find it before the query is executed in the debug mode. 您可以在调试模式下执行查询之前找到它。


I think what you're facing with is MongoDB driver projection of anonymous types. 我认为您面临的是匿名类型的MongoDB驱动程序投影。 It can't $project the document itself because it should map it to some field name . 它不能$ project文档本身,因为它应该将其映射到某些字段名称 So probably workaround on it would be just give naming to the properties inside the anonymous types: 因此,可能的解决方法是为匿名类型内的属性命名:

var q = A_Col.AsQueryable().Where(a => !a.Locked)
.Join(
   AB_Col.AsQueryable(),
   a => a.Id, 
   ab => ab.AId,
   (a, ab) => new
   {
      A = new { Id = a.Id, Name = a.Name, LastName = a.LastName }, // change here
      AB = new { AId = ab.AId, BId = ab.BId } // change here
   })
.Join(
   B_Col.AsQueryable(), 
   a_doc => a_doc.AB.BId,
   b => b.Id, 
   (a, b) => new { Id = a.A.Id, Name = a.A.Name, LastName = a.A.LastName, B = new { Name = b.Name, Id = b.Id } }) // change here
.GroupBy(doc => new { Id = doc.Id, Name = doc.Name, LastName = doc.LastName }) // change here
.Join(
    AC_Col.AsQueryable(),
    g => g.Key.Id,
    ac => ac.AId,
    (g, ac) => new
    {
       Id = g.Key.Id, // change here
       Name = g.Key.Name, // change here
       LastName = g.Key.LastName, // change here
       Bs = g.Select(x => x.B),
       AC = new { ac.AId, ac.CId }
    })
.Join(
    C_Col.AsQueryable(),
    gac => gac.AC.CId,
    c => c.Id,
    (gac, c) => new { Id = gac.Id, Name = gac.Name, LastName = gac.LastName, Bs = gac.Bs, C = new { Name = c.Name, Id = c.Id } }) // change here
.GroupBy(doc => new { Id = doc.Id, Name = doc.Name, LastName = doc.LastName, Bs = doc.Bs }) // change here
.Select(g => new { Id = g.Key.Id, Name = g.Key.Name, LastName = g.Key.LastName, Bs = g.Key.Bs, Cs = g.Select(x => x.C) }); // change here

The problem weren't at the properties' names but at a missing .Select . 问题不在于属性名称,而在于缺少.Select This is the working query (and it's so much shorter): 这是有效的查询(并且它要短得多):

var q = A_Col.AsQueryable().Where(a => !a.Locked)
  .Join(
      AB_Col.AsQueryable(), a => a.Id, ab => ab.AId,
      (a, ab) => new { A = new { a.Id, a.Name, a.LastName }, ab.BId })
  .Join(
      B_Col.AsQueryable(),
      a_doc => a_doc.BId,
      b => b.Id,
      (a, b) => new { a.A, B = new { b.Id, b.Name } })
  .GroupBy(doc => doc.A)
  .Select(g => new { A = g.Key, Bs = g.Select(i => i.B)}) // <----- THIS ONE HERE!!
  .Join(
      AC_Col.AsQueryable(),
      r => r.A.Id,
      ac => ac.AId,
      (r, ac) => new { r.A, r.Bs, ac.CId })
  .Join(
      C_Col.AsQueryable(),
      gac => gac.CId,
      c => c.Id,
      (gac, c) => new { gac.A, gac.Bs, C = new { c.Name, c.Id } })
  .GroupBy(doc => new { doc.A, doc.Bs })
  .Select(g => new { g.Key.A, g.Key.Bs, Cs = g.Select(i => i.C) });

I'm not sure why but it's working. 我不知道为什么,但是它正在工作。 Now I'm working on the LEFT JOIN. 现在我正在做LEFT JOIN。 :) :)

EDIT: After a lot of struggling I finally have the desired query with the LEFT JOIN s included. 编辑:经过很多努力后,我终于有了包含LEFT JOIN的所需查询。

 var q = from a in A_Col.AsQueryable()
     where !a.Locked
     join ab in AB_Col.AsQueryable() on a.Id equals ab.AId into j1
     from abi in j1.DefaultIfEmpty()
     select new {A = new {a.Id, a.Name, a.LastName }, abi.BId } into r1
     join b in B_Col.AsQueryable() on r1.BId equals b.Id into r2
     from bi in r2.DefaultIfEmpty()
     select new { r1.A, B = new { bi.Id, bi.Name } } into r2
     group r2 by r2.A into g1
     select new { A = g1.Key, Bs = g1.Select(i => new { Id = i.B.Id ?? "", Name = i.B.Name ?? ""  }) } into r3
     join ac in AC_Col.AsQueryable() on r3.A.Id equals ac.AId into j2
     from aci in j2.DefaultIfEmpty()
     select new { r3.A, r3.Bs, aci.CId } into r4
     join c in C_Col.AsQueryable() on r4.CId equals c.Id into j2
     from ci in j2.DefaultIfEmpty()
     select new { r4.A, r4.Bs, C = new { ci.Id, ci.Name } } into r5
     group r5 by new {r5.A, r5.Bs} into g2
     select new { g2.Key.A, g2.Key.Bs, Cs = g2.Select(i => new { Id = i.C.Id ?? "", Name = i.C.Name ?? ""  }) };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM