C#के साथ $ लुकअप को अलग करें




mongodb aggregation-framework (2)

मेरे पास निम्नलिखित MongoDb क्वेरी काम कर रही है:

db.Entity.aggregate(
    [
        {
            "$match":{"Id": "12345"}
        },
        {
            "$lookup": {
                "from": "OtherCollection",
                "localField": "otherCollectionId",
                "foreignField": "Id",
                "as": "ent"
            }
        },
        { 
            "$project": { 
                "Name": 1,
                "Date": 1,
                "OtherObject": { "$arrayElemAt": [ "$ent", 0 ] } 
            }
        },
        { 
            "$sort": { 
                "OtherObject.Profile.Name": 1
            } 
        }
    ]
)

यह उन वस्तुओं की सूची को पुनः प्राप्त करता है जो किसी अन्य संग्रह से एक मिलान वस्तु के साथ जुड़ती हैं।

क्या किसी को पता है कि मैं LINQ का उपयोग करके या इस सटीक स्ट्रिंग का उपयोग करके C # में इसका उपयोग कैसे कर सकता हूं?

मैंने निम्नलिखित कोड का उपयोग करने की कोशिश की, लेकिन यह QueryDocument और MongoCursor के प्रकारों को खोजने के लिए प्रतीत नहीं हो सकता है - मुझे लगता है कि उन्हें हटा दिया गया है?

BsonDocument document = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>("{ name : value }");
QueryDocument queryDoc = new QueryDocument(document);
MongoCursor toReturn = _connectionCollection.Find(queryDoc);

JSON को पार्स करने की कोई आवश्यकता नहीं है। यहां सब कुछ वास्तव में सीधे LINQ या Aggregate Fluent इंटरफेस के साथ किया जा सकता है।

बस कुछ प्रदर्शन वर्गों का उपयोग करना क्योंकि प्रश्न वास्तव में बहुत कुछ नहीं देता है।

सेट अप

मूल रूप से हमारे यहाँ दो संग्रह हैं, जा रहा है

संस्थाओं

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

और अन्य

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

और कुछ वर्गों के लिए उन्हें बांधने के लिए, बहुत ही मूल उदाहरण के रूप में:

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;
}

प्रश्नों

धाराप्रवाह इंटरफ़ेस

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();

सर्वर को भेजा गया अनुरोध:

[
  { "$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 } }
]

धाराप्रवाह इंटरफ़ेस के बाद से संभवतः सबसे आसान समझने के लिए मूल रूप से सामान्य BSON संरचना के समान है। $lookup चरण में सभी समान तर्क हैं और $arrayElemAt को First() साथ $arrayElemAt है। $sort आप बस एक BSON दस्तावेज़ या अन्य मान्य अभिव्यक्ति की आपूर्ति कर सकते हैं।

एक वैकल्पिक MongoDB 3.6 और इसके बाद के संस्करण के लिए एक उप-पाइपलाइन स्टेटमेंट के साथ $lookup का नया अभिव्यंजक रूप है।

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();

सर्वर को भेजा गया अनुरोध:

[ 
  { "$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 } }
]

धाराप्रवाह "बिल्डर" सीधे सिंटैक्स का समर्थन नहीं करता है, और न ही LINQ एक्सप्रेशन $expr ऑपरेटर का समर्थन करता है, हालांकि आप अभी भी BsonDocument और BsonArray या अन्य वैध अभिव्यक्तियों का उपयोग करके निर्माण कर सकते हैं। यहां हम BsonDocument बजाय पहले की तरह दिखाए गए अभिव्यक्ति का उपयोग करके $sort को लागू करने के लिए $sort BsonDocument परिणाम "टाइप" करते हैं।

अन्य उपयोगों के अलावा, एक "उप-पाइपलाइन" का एक प्राथमिक कार्य $lookup के लक्ष्य सरणी में लौटे दस्तावेजों को कम करना है। इसके अलावा, यहाँ $unwind वास्तव में सर्वर के निष्पादन पर $lookup स्टेटमेंट में "मर्ज" होने का एक उद्देश्य प्रदान करता है, इसलिए यह आमतौर पर परिणामी सरणी के पहले तत्व को हथियाने की तुलना में अधिक कुशल है।

क्वेरी करने योग्य GroupJoin

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);

सर्वर को भेजा गया अनुरोध:

[ 
  { "$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 } }
]

यह लगभग समान है लेकिन बस अलग-अलग इंटरफ़ेस का उपयोग कर रहा है और थोड़ा अलग BSON स्टेटमेंट का उत्पादन करता है, और वास्तव में केवल कार्यात्मक कथनों में सरलीकृत नामकरण के कारण। यह SelectMany() से उत्पादित $unwind का उपयोग करने की अन्य संभावना को 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);

सर्वर को भेजा गया अनुरोध:

[
  { "$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 } }
]

आम तौर पर $unwind लुकिंग के बाद सीधे एक $unwind , वास्तव में एकत्रीकरण ढांचे के लिए एक "अनुकूलित पैटर्न" है । हालाँकि .NET ड्राइवर इस संयोजन को "as" पर निहित नामकरण का उपयोग करने के बजाय बीच में एक $project को मजबूर करके गड़बड़ करता है। यदि नहीं, तो यह वास्तव में $arrayElemAt से बेहतर है जब आप जानते हैं कि आपके पास "एक" संबंधित परिणाम है। यदि आप $unwind "कोलेसेंस" $unwind चाहते हैं, तो आप धाराप्रवाह इंटरफ़ेस, या बाद में प्रदर्शित एक अलग रूप का उपयोग करके बेहतर हैं।

वनीय प्राकृतिक

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;

सर्वर को भेजा गया अनुरोध:

[
  { "$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 } }
]

सभी सुंदर परिचित और वास्तव में सिर्फ कार्यात्मक नामकरण करने के लिए नीचे। बस के रूप में $unwind विकल्प का उपयोग कर के साथ:

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;

सर्वर को भेजा गया अनुरोध:

[ 
  { "$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 } }
]

जो वास्तव में "अनुकूलित कोलेसेंस" फॉर्म का उपयोग कर रहा है। अनुवादक अभी भी एक $project को जोड़ने पर जोर देता है क्योंकि हमें बयान को वैध बनाने के लिए मध्यवर्ती select आवश्यकता है।

सारांश

तो मूल रूप से वास्तव में समान परिणामों के साथ एक ही क्वेरी स्टेटमेंट क्या है, इस पर पहुंचने के लिए काफी कुछ तरीके हैं। जब तक आप JSON को BsonDocument फॉर्म में पार्स कर सकते हैं और इसे धाराप्रवाह Aggregate() कमांड में फीड कर सकते हैं, तो आमतौर पर प्राकृतिक बिल्डरों या LINQ इंटरफेस का उपयोग करना बेहतर होता है क्योंकि वे एक ही स्टेटमेंट पर आसानी से मैप करते हैं।

$arrayElemAt विकल्प को बड़े पैमाने पर दिखाया गया है, क्योंकि "एकवचन" से मेल खाते हुए भी कि " $arrayElemAt " रूप वास्तव में कहीं अधिक इष्टतम है, फिर "पहली" सरणी तत्व लेने के लिए $arrayElemAt का उपयोग करना। यह और भी महत्वपूर्ण हो जाता है क्योंकि बीएसओएन लिमिट जैसी चीजों पर विचार किया जाता है, जहां $lookup टारगेट सरणी पेरेंट डॉक्यूमेंट को 16MB से आगे फिल्टर किए बिना पार कर सकती है। एग्रीगेट $ लुकिंग पर यहां एक और पोस्ट है। मिलान पाइपलाइन में दस्तावेजों का कुल आकार अधिकतम दस्तावेज़ आकार से अधिक है जहां मैं वास्तव में चर्चा करता हूं कि इस तरह के विकल्पों या अन्य Lookup() सिंटैक्स का उपयोग करके हिट सीमा से बचने के लिए इस समय केवल धाराप्रवाह इंटरफ़ेस उपलब्ध है। ।


यह कैसे MongoDB.Entities साथ करने के लिए है। ऐसे मामलों में जहां दो इकाइयां एक-से-कई या कई-से-कई संबंधों में हैं, आप नीचे दिखाए गए अनुसार मैन्युअल रूप से जुड़ने के बिना रिवर्स रिलेशनशिप एक्सेस प्राप्त कर सकते हैं। [अस्वीकरण: मैं पुस्तकालय का लेखक हूं]

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

namespace 
{
    public class Program
    {
        public class Author : Entity
        {
            public string Name { get; set; }
            public Many<Book> Books { get; set; }

            public Author() => this.InitOneToMany(() => Books);
        }

        public class Book : Entity
        {
            public string Title { get; set; }
        }

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

            var book = new Book { Title = "The Power Of Now" };
            book.Save();

            var author = new Author { Name = "Eckhart Tolle" };
            author.Save();

            author.Books.Add(book);

            //build a query for finding all books that has Power in the title.
            var bookQuery = DB.Queryable<Book>()
                              .Where(b => b.Title.Contains("Power"));

            //find all the authors of books that has a title with Power in them
            var authors = author.Books
                                .ParentsQueryable<Author>(bookQuery); //also can pass in an ID or array of IDs

            //get the result
            var result = authors.ToArray();

            //output the aggregation pipeline
            Console.WriteLine(authors.ToString());


            Console.ReadKey();
        }
    }
}




mongodb-.net-driver