MongoDB
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> MongoDB

Tổng hợp $ tra cứu với C #

Không cần phải phân tích cú pháp JSON. Mọi thứ ở đây thực sự có thể được thực hiện trực tiếp với LINQ hoặc giao diện Aggregate Fluent.

Chỉ sử dụng một số lớp trình diễn vì câu hỏi không thực sự mang lại nhiều điều để tiếp tục.

Thiết lập

Về cơ bản, chúng tôi có hai bộ sưu tập ở đây, là

thực thể

{ "_id" : ObjectId("5b08ceb40a8a7614c70a5710"), "name" : "A" }
{ "_id" : ObjectId("5b08ceb40a8a7614c70a5711"), "name" : "B" }

những người khác

{
        "_id" : ObjectId("5b08cef10a8a7614c70a5712"),
        "entity" : ObjectId("5b08ceb40a8a7614c70a5710"),
        "name" : "Sub-A"
}
{
        "_id" : ObjectId("5b08cefd0a8a7614c70a5713"),
        "entity" : ObjectId("5b08ceb40a8a7614c70a5711"),
        "name" : "Sub-B"
}

Và một vài lớp để ràng buộc chúng, giống như những ví dụ rất cơ bản:

public class Entity
{
  public ObjectId id;
  public string name { get; set; }
}

public class Other
{
  public ObjectId id;
  public ObjectId entity { get; set; }
  public string name { get; set; }
}

public class EntityWithOthers
{
  public ObjectId id;
  public string name { get; set; }
  public IEnumerable<Other> others;
}

 public class EntityWithOther
{
  public ObjectId id;
  public string name { get; set; }
  public Other others;
}

Truy vấn

Giao diện thông thạo

var listNames = new[] { "A", "B" };

var query = entities.Aggregate()
    .Match(p => listNames.Contains(p.name))
    .Lookup(
      foreignCollection: others,
      localField: e => e.id,
      foreignField: f => f.entity,
      @as: (EntityWithOthers eo) => eo.others
    )
    .Project(p => new { p.id, p.name, other = p.others.First() } )
    .Sort(new BsonDocument("other.name",-1))
    .ToList();

Yêu cầu được gửi đến máy chủ:

[
  { "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
  { "$lookup" : { 
    "from" : "others",
    "localField" : "_id",
    "foreignField" : "entity",
    "as" : "others"
  } }, 
  { "$project" : { 
    "id" : "$_id",
    "name" : "$name",
    "other" : { "$arrayElemAt" : [ "$others", 0 ] },
    "_id" : 0
  } },
  { "$sort" : { "other.name" : -1 } }
]

Có lẽ là dễ hiểu nhất vì giao diện thông thạo về cơ bản giống với cấu trúc BSON chung. $lookup giai đoạn có tất cả các đối số giống nhau và $arrayElemAt được biểu diễn bằng First() . Đối với $sort bạn có thể chỉ cần cung cấp tài liệu BSON hoặc biểu thức hợp lệ khác.

Thay thế là hình thức biểu đạt mới hơn của $lookup với một tuyên bố đường ống phụ cho MongoDB 3.6 trở lên.

BsonArray subpipeline = new BsonArray();

subpipeline.Add(
  new BsonDocument("$match",new BsonDocument(
    "$expr", new BsonDocument(
      "$eq", new BsonArray { "$$entity", "$entity" }  
    )
  ))
);

var lookup = new BsonDocument("$lookup",
  new BsonDocument("from", "others")
    .Add("let", new BsonDocument("entity", "$_id"))
    .Add("pipeline", subpipeline)
    .Add("as","others")
);

var query = entities.Aggregate()
  .Match(p => listNames.Contains(p.name))
  .AppendStage<EntityWithOthers>(lookup)
  .Unwind<EntityWithOthers, EntityWithOther>(p => p.others)
  .SortByDescending(p => p.others.name)
  .ToList();

Yêu cầu được gửi đến máy chủ:

[ 
  { "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
  { "$lookup" : {
    "from" : "others",
    "let" : { "entity" : "$_id" },
    "pipeline" : [
      { "$match" : { "$expr" : { "$eq" : [ "$$entity", "$entity" ] } } }
    ],
    "as" : "others"
  } },
  { "$unwind" : "$others" },
  { "$sort" : { "others.name" : -1 } }
]

Fluent "Builder" chưa hỗ trợ cú pháp trực tiếp, cũng như Biểu thức LINQ không hỗ trợ $expr , tuy nhiên, bạn vẫn có thể xây dựng bằng cách sử dụng BsonDocumentBsonArray hoặc các biểu thức hợp lệ khác. Ở đây chúng tôi cũng "gõ" $unwind dẫn đến việc áp dụng $sort sử dụng một biểu thức thay vì một BsonDocument như được hiển thị trước đó.

Ngoài các mục đích sử dụng khác, nhiệm vụ chính của "đường ống con" là giảm bớt các tài liệu được trả về trong mảng mục tiêu của $lookup . Cũng là $unwind ở đây phục vụ mục đích thực sự được "hợp nhất" vào $lookup trên thực thi máy chủ, vì vậy điều này thường hiệu quả hơn là chỉ lấy phần tử đầu tiên của mảng kết quả.

Tham gia nhóm có thể truy vấn

var query = entities.AsQueryable()
    .Where(p => listNames.Contains(p.name))
    .GroupJoin(
      others.AsQueryable(),
      p => p.id,
      o => o.entity,
      (p, o) => new { p.id, p.name, other = o.First() }
    )
    .OrderByDescending(p => p.other.name);

Yêu cầu được gửi đến máy chủ:

[ 
  { "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
  { "$lookup" : {
    "from" : "others",
    "localField" : "_id",
    "foreignField" : "entity",
    "as" : "o"
  } },
  { "$project" : {
    "id" : "$_id",
    "name" : "$name",
    "other" : { "$arrayElemAt" : [ "$o", 0 ] },
    "_id" : 0
  } },
  { "$sort" : { "other.name" : -1 } }
]

Điều này gần như giống hệt nhau nhưng chỉ sử dụng giao diện khác và tạo ra một câu lệnh BSON hơi khác và thực sự chỉ vì cách đặt tên đơn giản trong các câu lệnh chức năng. Điều này mang lại một khả năng khác là chỉ cần sử dụng $unwind như được tạo ra từ một SelectMany() :

var query = entities.AsQueryable()
  .Where(p => listNames.Contains(p.name))
  .GroupJoin(
    others.AsQueryable(),
    p => p.id,
    o => o.entity,
    (p, o) => new { p.id, p.name, other = o }
  )
  .SelectMany(p => p.other, (p, other) => new { p.id, p.name, other })
  .OrderByDescending(p => p.other.name);

Yêu cầu được gửi đến máy chủ:

[
  { "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
  { "$lookup" : {
    "from" : "others",
    "localField" : "_id",
    "foreignField" : "entity",
    "as" : "o"
  }},
  { "$project" : {
    "id" : "$_id",
    "name" : "$name",
    "other" : "$o",
    "_id" : 0
  } },
  { "$unwind" : "$other" },
  { "$project" : {
    "id" : "$id",
    "name" : "$name",
    "other" : "$other",
    "_id" : 0
  }},
  { "$sort" : { "other.name" : -1 } }
]

Thường đặt $unwind trực tiếp theo dõi $lookup thực sự là một "mẫu được tối ưu hóa" cho khung tổng hợp. Tuy nhiên, trình điều khiển .NET sẽ làm rối tung điều này trong sự kết hợp này bằng cách buộc một $project ở giữa thay vì sử dụng tên ngụ ý trên "as" . Nếu không phải vì điều đó, điều này thực sự tốt hơn $arrayElemAt khi bạn biết bạn có "một" kết quả liên quan. Nếu bạn muốn $unwind "kết hợp", thì tốt hơn hết bạn nên sử dụng giao diện thông thạo hoặc một hình thức khác như được trình bày ở phần sau.

Tự nhiên có thể xếp hạng

var query = from p in entities.AsQueryable()
            where listNames.Contains(p.name) 
            join o in others.AsQueryable() on p.id equals o.entity into joined
            select new { p.id, p.name, other = joined.First() }
            into p
            orderby p.other.name descending
            select p;

Yêu cầu được gửi đến máy chủ:

[
  { "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
  { "$lookup" : {
    "from" : "others",
    "localField" : "_id",
    "foreignField" : "entity",
    "as" : "joined"
  } },
  { "$project" : {
    "id" : "$_id",
    "name" : "$name",
    "other" : { "$arrayElemAt" : [ "$joined", 0 ] },
    "_id" : 0
  } },
  { "$sort" : { "other.name" : -1 } }
]

Tất cả đều khá quen thuộc và thực sự chỉ là đặt tên theo chức năng. Cũng giống như khi sử dụng $unwind tùy chọn:

var query = from p in entities.AsQueryable()
            where listNames.Contains(p.name) 
            join o in others.AsQueryable() on p.id equals o.entity into joined
            from sub_o in joined.DefaultIfEmpty()
            select new { p.id, p.name, other = sub_o }
            into p
            orderby p.other.name descending
            select p;

Yêu cầu được gửi đến máy chủ:

[ 
  { "$match" : { "name" : { "$in" : [ "A", "B" ] } } },
  { "$lookup" : {
    "from" : "others",
    "localField" : "_id",
    "foreignField" : "entity",
    "as" : "joined"
  } },
  { "$unwind" : { 
    "path" : "$joined", "preserveNullAndEmptyArrays" : true
  } }, 
  { "$project" : { 
    "id" : "$_id",
    "name" : "$name",
    "other" : "$joined",
    "_id" : 0
  } }, 
  { "$sort" : { "other.name" : -1 } }
]

Mà thực sự đang sử dụng hình thức "liên kết tối ưu hóa". Người dịch vẫn khăng khăng thêm một $project vì chúng ta cần select trung gian để làm cho tuyên bố hợp lệ.

Tóm tắt

Vì vậy, có khá nhiều cách để đi đến cơ bản là cùng một câu lệnh truy vấn với kết quả chính xác như nhau. Trong khi bạn "có thể" phân tích cú pháp JSON thành BsonDocument hình thành và cấp dữ liệu này vào Aggregate() thông thạo , nói chung tốt hơn là sử dụng các trình xây dựng tự nhiên hoặc các giao diện LINQ vì chúng dễ dàng ánh xạ vào cùng một câu lệnh.

Các tùy chọn với $unwind phần lớn được hiển thị bởi vì ngay cả với một đối sánh "số ít" thì dạng "kết hợp" thực sự tối ưu hơn nhiều khi sử dụng $arrayElemAt để lấy phần tử mảng "đầu tiên". Điều này thậm chí còn trở nên quan trọng hơn khi xem xét những thứ như Giới hạn BSON nơi $lookup mảng đích có thể khiến tài liệu mẹ vượt quá 16MB mà không cần lọc thêm. Có một bài đăng khác ở đây trên Aggregate $ lookup Tổng kích thước của tài liệu trong đường dẫn phù hợp vượt quá kích thước tài liệu tối đa, nơi tôi thực sự thảo luận về cách tránh giới hạn đó bị ảnh hưởng bằng cách sử dụng các tùy chọn như vậy hoặc Lookup() khác cú pháp chỉ có sẵn cho giao diện thông thạo tại thời điểm này.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoDB C #:ID Serialization mẫu tốt nhất

  2. Làm cách nào để triển khai lược đồ này trong MongoDB?

  3. Lặp qua tất cả các bộ sưu tập Mongo và thực hiện truy vấn

  4. Nhóm mảng sau khi thư giãn và khớp

  5. Tự động kiểm tra tình trạng cơ sở dữ liệu