sql >> データベース >  >> NoSQL >> MongoDB

$ lookupをC#で集計する

    JSONを解析する必要はありません。ここでのすべては、実際にはLINQまたはAggregateFluentインターフェースのいずれかを使用して直接実行できます。

    質問はあまり進んでいないので、いくつかのデモンストレーションクラスを使用するだけです。

    セットアップ

    基本的に、ここには2つのコレクションがあります。

    エンティティ

    { "_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()で表されます 。 $sortの場合 BSONドキュメントまたはその他の有効な式を指定するだけです。

    別の方法は、$lookupの新しい表現形式です。 MongoDB3.6以降のサブパイプラインステートメントを使用します。

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

    Fluent "Builder"はまだ構文を直接サポートしていません。また、LINQ式は$exprをサポートしていません。 演算子。ただし、BsonDocumentを使用して作成することもできます。 およびBsonArray または他の有効な式。ここでは、$unwindも「入力」します $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ステートメントを生成します。これは、機能ステートメントの名前が単純化されているためです。これにより、$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を配置します $lookupの直後 実際には、集約フレームワークの「最適化されたパターン」です。ただし、.NETドライバーは、$projectを強制することにより、この組み合わせでこれを台無しにします。 "as"で暗黙の命名を使用するのではなく、その間に 。そうでない場合、これは実際には$arrayElemAtよりも優れています 「1つの」関連する結果があることがわかっている場合。 $unwindが必要な場合 「合体」の場合は、流暢なインターフェイスを使用するか、後で説明するように別の形式を使用することをお勧めします。

    Querable Natural

    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インターフェイスを使用することをお勧めします。

    $unwindのオプション $arrayElemAtを使用するよりも、「合体」形式の方が実際にははるかに最適であるという「特異な」一致があっても、主に示されています。 「最初の」配列要素を取得します。これは、$lookupであるBSON制限などを考慮するとさらに重要になります。 ターゲット配列により、さらにフィルタリングせずに親ドキュメントが16MBを超える可能性があります。 Aggregate $ lookupに関する別の投稿があります。一致するパイプライン内のドキュメントの合計サイズが最大ドキュメントサイズを超えています。このようなオプションやその他のLookup()を使用して、制限に達するのを回避する方法について実際に説明しています。 現時点でのみ流暢なインターフェースで使用できる構文。




    1. リモートサーバーからのMongodump

    2. Node.jsのソケットを介したRedis接続

    3. クライアントIPをredisで取得する方法はありますか?

    4. MongoDBは浮動小数点型をサポートしていますか?