web-dev-qa-db-ja.com

参照モデルのフィールドによるモデルのMongooseネストされたクエリ

Stackoverflowに関するこのトピックには多くのQ/Aがあるようですが、どこにも正確な答えを見つけることができないようです。

私が持っているもの:

CompanyモデルとPersonモデルがあります。

var mongoose = require('mongoose');
var PersonSchema = new mongoose.Schema{
                        name: String, 
                        lastname: String};

// company has a reference to Person
var CompanySchema = new mongoose.Schema{
                        name: String, 
                        founder: {type:Schema.ObjectId, ref:Person}};

必要なもの:

姓が「Robertson」の人が設立したすべての企業を検索

私が試したこと:

Company.find({'founder.id': 'Robertson'}, function(err, companies){
    console.log(companies); // getting an empty array
});

次に、Personは埋め込まれていないが参照されていると考えたので、populateを使用してfounder-Personにデータを入力し、find with 'Robertson' lastnameを使用してみました

// 1. retrieve all companies
// 2. populate their founders
// 3. find 'Robertson' lastname in populated Companies
Company.find({}).populate('founder')
       .find({'founder.lastname': 'Robertson'})
       .exec(function(err, companies) {
        console.log(companies); // getting an empty array again
    });

それでも、PersonのIDを文字列として使用して会社をクエリできます。しかし、あなたが理解できるように、それは私が正確に欲しいものではありません

Company.find({'founder': '525cf76f919dc8010f00000d'}, function(err, companies){
    console.log(companies); // this works
});
22
AzaFromKaza

MongoDBは結合をサポートしていないため、単一のクエリでこれを行うことはできません。代わりに、それをいくつかのステップに分割する必要があります。

// Get the _ids of people with the last name of Robertson.
Person.find({lastname: 'Robertson'}, {_id: 1}, function(err, docs) {

    // Map the docs into an array of just the _ids
    var ids = docs.map(function(doc) { return doc._id; });

    // Get the companies whose founders are in that set.
    Company.find({founder: {$in: ids}}, function(err, docs) {
        // docs contains your answer
    });
});
39
JohnnyHK

私はこれにかなり遅れています:pしかし、私は同じような答えを探していただけで、誰かが同じ理由でこれを見つけた場合に備えて、私が思いついたものを共有したいと思いました。

Mongooseクエリでこれを実現する方法を見つけることができませんでしたが、 MongoDB集計パイプライン を使用して機能すると思います

探しているクエリを取得するには、次のようにします。

_const result=await Company.aggregate([
    {$lookup: {
        from: 'persons', 
        localField: 'founder', 
        foreignField: '_id', 
        as: 'founder'}
    },
    {$unwind: {path: '$founder'}},
    {$match: {'founder.lastname', 'Robertson'}}
]);
_

_$lookup_.populate()のように機能し、参照を実際のデータに置き換えます。ただし、複数のドキュメントの照合に使用できるため、配列を返します。

_$unwind_ は、配列から項目を削除します。この場合、単一の要素の配列をフィールドに変換します。

_$match_ 次に、実際の動作を実行し、クエリに一致するドキュメントのみを返します。必要に応じて、厳密な等価よりも複雑なマッチングを行うこともできます。

一般に、集約パイプラインの動作方法は、一致するドキュメントを、必要なものが手に入るまで継続的にフィルタリング/変更することです。

私はこれでパフォーマンスを確認していませんが、サーバー側で不要な結果を除外するよりも、Mongoに作業を実行させることをお勧めします。

唯一の欠点は、パイプラインは通常ドキュメントの形状を変更するため、結果はマングースモデルではなくオブジェクトの配列になることです。したがって、返されたデータに対してモデルのメソッドを使用することはできません。

1
sbrass

最近誰かがこれに遭遇した場合に備えて、Mongooseは現在、Populateと呼ばれる機能を備えた結合のような機能をサポートしています。

Mongooseのドキュメントから:

Story.findOne({ 
    title: 'Casino Royale' 
}).populate('author').exec(function (err, story) {
    if (err) return handleError(err);
    console.log('The author is %s', story.author.name);
    // prints "The author is Ian Fleming"
});

http://mongoosejs.com/docs/populate.html

1
evanmcd