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

配列をフィルタリングして関連コンテンツを入力するマングースクエリ

    ここで一致を「投影」する必要があります。これは、MongoDBクエリが行うのは、「少なくとも1つの要素」を含む「ドキュメント」を探すことだけだからです。 つまり、「より大きい」 あなたが求めた状態。

    したがって、「配列」のフィルタリングは、「クエリ」条件と同じではありません。

    単純な「プロジェクション」は、「最初の」一致したアイテムをその条件に戻すだけです。したがって、それはおそらくあなたが望むものではありませんが、例として:

    Order.find({ "articles.quantity": { "$gte": 5 } })
        .select({ "articles.$": 1 })
        .populate({
            "path": "articles.article",
            "match": { "price": { "$lte": 500 } }
        }).exec(function(err,orders) {
           // populated and filtered twice
        }
    )
    

    その「一種の」はあなたが望むことをします、しかし問題は本当にそれがせいぜい1つしか戻らないということになるでしょう "articles"内の要素 配列。

    これを適切に行うには、.aggregate()が必要です。 配列の内容をフィルタリングします。理想的には、これはMongoDB3.2と$filterを使用して行われます。 。ただし、.populate()には特別な方法もあります。 ここ:

    Order.aggregate(
        [
            { "$match": { "artciles.quantity": { "$gte": 5 } } },
            { "$project": {
                "orderdate": 1,
                "articles": {
                    "$filter": {
                        "input": "$articles",
                        "as": "article",
                        "cond": {
                           "$gte": [ "$$article.quantity", 5 ]
                        }
                    }
                },
                "__v": 1
            }}
        ],
        function(err,orders) {
            Order.populate(
                orders.map(function(order) { return new Order(order) }),
                {
                    "path": "articles.article",
                    "match": { "price": { "$lte": 500 } }
                },
                function(err,orders) {
                    // now it's all populated and mongoose documents
                }
            )
        }
    )
    

    したがって、ここで行われるのは、配列の実際の「フィルタリング」が.aggregate()内で行われることです。 ステートメントですが、もちろん、.aggregate()の1つの側面があるため、これによる結果は「マングースドキュメント」ではなくなります。 これは、ドキュメント構造を「変更」できるということです。このため、マングースはその場合を「推定」し、「プレーンオブジェクト」を返すだけです。

    $projectが表示されるので、これは実際には問題ではありません。 ステージでは、実際には、定義されたスキーマに従って、ドキュメントに存在する同じフィールドをすべて要求しています。したがって、これは単なる「プレーンオブジェクト」ですが、マングースドキュメントに「キャスト」するのに問題はありません。

    ここで.map() 変換された「ドキュメント」の配列を返すため、次のステージで重要になります。

    ここで、Model.populate()を呼び出します。 その後、「マングース文書の配列」でさらに「母集団」を実行できます。

    その結果、最終的にあなたが望むものになります。

    3.2.xより古いバージョンのMongoDB

    ここで実際に変更されるのは集約パイプラインだけです。簡潔にするために含める必要があるのはそれだけです。

    MongoDB 2.6 -$mapの組み合わせで配列をフィルタリングできます および$setDifference 。結果は「セット」ですが、マングースが_idを作成する場合は問題ありません。 デフォルトでは、すべてのサブドキュメント配列のフィールド:

        [
            { "$match": { "artciles.quantity": { "$gte": 5 } } },
            { "$project": {
                "orderdate": 1,
                "articles": {
                    "$setDiffernce": [
                       { "$map": {
                          "input": "$articles",
                          "as": "article",
                          "in": {
                             "$cond": [
                                 { "$gte": [ "$$article.price", 5 ] },
                                 "$$article",
                                 false
                             ]
                          }
                       }},
                       [false]
                    ]
                },
                "__v": 1
            }}
        ],
    

    それより古いリビジョンでは、$unwindを使用する必要があります :

        [
            { "$match": { "artciles.quantity": { "$gte": 5 } }},
            { "$unwind": "$articles" },
            { "$match": { "artciles.quantity": { "$gte": 5 } }},
            { "$group": {
              "_id": "$_id",
              "orderdate": { "$first": "$orderdate" },
              "articles": { "$push": "$articles" },
              "__v": { "$first": "$__v" }
            }}
        ],
    

    $ lookup Alternative

    もう1つの方法は、代わりに「サーバー」ですべてを実行することです。これは$lookupのオプションです MongoDB 3.2以降のバージョン:

    Order.aggregate(
        [
            { "$match": { "artciles.quantity": { "$gte": 5 } }},
            { "$project": {
                "orderdate": 1,
                "articles": {
                    "$filter": {
                        "input": "$articles",
                        "as": "article",
                        "cond": {
                           "$gte": [ "$$article.quantity", 5 ]
                        }
                    }
                },
                "__v": 1
            }},
            { "$unwind": "$articles" },
            { "$lookup": {
                "from": "articles",
                "localField": "articles.article",
                "foreignField": "_id",
                "as": "articles.article"
            }},
            { "$unwind": "$articles.article" },
            { "$group": {
              "_id": "$_id",
              "orderdate": { "$first": "$orderdate" },
              "articles": { "$push": "$articles" },
              "__v": { "$first": "$__v" }
            }},
            { "$project": {
                "orderdate": 1,
                "articles": {
                    "$filter": {
                        "input": "$articles",
                        "as": "article",
                        "cond": {
                           "$lte": [ "$$article.article.price", 500 ]
                        }
                    }
                },
                "__v": 1
            }}
        ],
        function(err,orders) {
    
        }
    )
    

    これらは単なるドキュメントですが、.populate()から得られる結果とまったく同じです。 アプローチ。そしてもちろん、本当に必要な場合は、いつでもマングースのドキュメントに「キャスト」して、すべての場合に再度「キャスト」することができます。

    「最短」パス

    これは実際には、「クエリ」が配列コンテンツを「フィルタリング」することを意図していないことを基本的に「受け入れる」という元のステートメントに戻ります。 .populate() これは単なる別の「クエリ」であり、便宜上「ドキュメント」に詰め込まれているため、喜んでそうすることができます。

    したがって、元のドキュメント配列内の追加の配列メンバーを削除して帯域幅の「バケットロード」を実際に保存していない場合は、.filter() 後処理コードでそれらを出します:

    Order.find({ "articles.quantity": { "$gte": 5 } })
        .populate({
            "path": "articles.article",
            "match": { "price": { "$lte": 500 } }
        }).exec(function(err,orders) {
            orders = orders.filter(function(order) {
                order.articles = order.articles.filter(function(article) {
                    return (
                        ( article.quantity >= 5 ) &&
                        ( article.article != null )
                    )
                });
                return order.aricles.length > 0;
            })
    
            // orders has non matching entries removed            
        }
    )
    


    1. データストアのためにRedisからMongoDBにデータを永続化する

    2. nodejsからmongodbまたはmongooseへの動的データベース接続

    3. ネイティブMongoDBマスキング(3番目の方法)

    4. .NET Coreの依存性注入では、 `StackExchange.Redis.ConnectionMultiplexer`を`AddSingleton`または`AddScope`にする必要がありますか?