web-dev-qa-db-ja.com

MongoDBの複数のコレクションを検索する

私はMongoDBの理論と、結合をサポートしていないという事実を知っており、埋め込みドキュメントを使用するか、可能な限り非正規化する必要がありますが、ここにあります:

次のような複数のドキュメントがあります。

  • 郊外を埋め込んでいるが、次のものも持っているユーザー:名、姓
  • 州を埋め込む郊外
  • Schoolを埋め込むChildはUserに属しますが、姓、名も持っています

例:

Users:
{ _id: 1, first_name: 'Bill', last_name: 'Gates', suburb: 1 }
{ _id: 2, first_name: 'Steve', last_name: 'Jobs', suburb: 3 }

Suburb:
{ _id: 1, name: 'Suburb A', state: 1 }
{ _id: 2, name: 'Suburb B', state: 1 }
{ _id: 3, name: 'Suburb C', state: 3 }

State:
{ _id: 1, name: 'LA' }
{ _id: 3, name: 'NY' }

Child:
{ _id: 1, _user_id: 1, first_name: 'Little Billy', last_name: 'Gates' }
{ _id: 2, _user_id: 2, first_name: 'Little Stevie', last_name: 'Jobs' }

実装する必要がある検索は次のとおりです。

  • ユーザーと子供の名、姓
  • ユーザーからの状態

私はそれを達成するために複数のクエリを実行する必要があることを知っていますが、どのようにそれを達成できますか? mapReduceまたは集計で?

解決策を教えてください。

MapReduceを使おうとしましたが、state_idを含むユーザーからのドキュメントを取得できなかったので、ここで取り上げました。

37
Adrian Istrate

この答えは時代遅れです。バージョン3.2以降、MongoDBは $ lookup 集計演算子を使用した左外部結合のサポートを制限しています

MongoDBは、複数のコレクション(期間)にまたがるクエリを実行しません。複数のコレクションのデータを結合する必要がある場合は、複数のクエリを実行して、アプリケーションレベルでデータを結合する必要があります。

  1. クエリコレクションA
  2. 結果から二次キーを取得し、配列に入れます
  3. その配列を $ in-operator の値として渡すクエリコレクションB
  4. アプリケーション層でプログラムで両方のクエリの結果を結合します

これを行う必要があるのは、通常よりもむしろ例外であるべきです。そのようなJOINを頻繁にエミュレートする必要がある場合は、データベーススキーマを設計するときにまだリレーショナルすぎると考えているか、またはデータがMongoDBのドキュメントベースのストレージ概念に適していないことを意味します。

38
Philipp

スキーマ設計に非正規化アプローチを採用すると、MongoDBが理解しやすくなります。つまり、要求元のクライアントアプリケーションがドキュメントを理解する方法でドキュメントを構造化します。基本的に、ドキュメントを ドメインオブジェクト としてモデル化し、アプリケーションで処理します。この方法でデータをモデル化すると、結合はそれほど重要ではなくなります。データを単一のコレクションに非正規化した方法を検討してください。

{  
    _id: 1, 
    first_name: 'Bill', 
    last_name: 'Gates', 
    suburb: 'Suburb A',
    state: 'LA',
    child : [ 3 ]
}

{ 
    _id: 2, 
    first_name: 'Steve', 
    last_name: 'Jobs', 
    suburb: 'Suburb C',
    state 'NY',
    child: [ 4 ] 
}
{ 
    _id: 3, 
    first_name: 'Little Billy', 
    last_name: 'Gates',
    suburb: 'Suburb A',
    state: 'LA',
    parent : [ 1 ]
}

{
    _id: 4, 
    first_name: 'Little Stevie', 
    last_name: 'Jobs'
    suburb: 'Suburb C',
    state 'NY',
    parent: [ 2 ]
}

最初の利点は、このスキーマの照会がはるかに簡単であることです。さらに、アドレスフィールドの更新は、フィールドが単一のドキュメントに埋め込まれているため、個々のPersonエンティティと一致するようになりました。親と子の間の双方向の関係にも注目してください。これにより、このコレクションは単なる個人のコレクションではありません。親子関係は、このコレクションも ソーシャルグラフ であることを意味します。 MongoDBのスキーマ設計 について考えるときに役立つ可能性のあるリソースを次に示します。

16
blimpyacht

指定した条件に一致するすべてのレコードの配列を返し、現在のデータベースのすべてのコレクションを検索するJavaScript関数を次に示します。

function searchAll(query,fields,sort) {
    var all = db.getCollectionNames();
    var results = [];
    for (var i in all) {
        var coll = all[i];
        if (coll == "system.indexes") continue;
        db[coll].find(query,fields).sort(sort).forEach(
            function (rec) {results.Push(rec);} );
    }
    return results;
}

Mongo Shellから、関数をコピーして貼り付け、次のように呼び出すことができます。

> var recs = searchAll({filename:{$ regex: '。pdf $'}}、{moddate:1、filename:1、_id:0}、{filename:1})> recs

10
Brian Moquin

そのため、mongodbで結合が可能になり、 $lookup および $facet ここでの集約は、おそらく複数のコレクションで見つけるための最良の方法です

db.collection.aggregate([
  { "$limit": 1 },
  { "$facet": {
    "c1": [
      { "$lookup": {
        "from": Users.collection.name,
        "pipeline": [
          { "$match": { "first_name": "your_search_data" } }
        ],
        "as": "collection1"
      }}
    ],
    "c2": [
      { "$lookup": {
        "from": State.collection.name,
        "pipeline": [
          { "$match": { "name": "your_search_data" } }
        ],
        "as": "collection2"
      }}
    ],
    "c3": [
      { "$lookup": {
        "from": State.collection.name,
        "pipeline": [
          { "$match": { "name": "your_search_data" } }
        ],
        "as": "collection3"
      }}
    ]
  }},
  { "$project": {
    "data": {
      "$concatArrays": [ "$c1", "$c2", "$c3" ]
    }
  }},
  { "$unwind": "$data" },
  { "$replaceRoot": { "newRoot": "$data" } }
])
7
Ashh

@ brian-moquinなどに基づいて、単純なキーワードでキー(フィールド)全体でコレクション全体を検索する一連の関数を作成しました。

それは私の要点です。 https://Gist.github.com/fkiller/005dc8a07eaa3321110b3e5753dda71b

詳細については、最初にすべてのキーを収集する関数を作成しました。

_function keys(collectionName) {
    mr = db.runCommand({
        'mapreduce': collectionName,
        'map': function () {
            for (var key in this) { emit(key, null); }
        },
        'reduce': function (key, stuff) { return null; },
        'out': 'my_collection' + '_keys'
    });
    return db[mr.result].distinct('_id');
}
_

次に、キー配列から_$or_クエリを生成します。

_function createOR(fieldNames, keyword) {
    var query = [];
    fieldNames.forEach(function (item) {
        var temp = {};
        temp[item] = { $regex: '.*' + keyword + '.*' };
        query.Push(temp);
    });
    if (query.length == 0) return false;
    return { $or: query };
}
_

以下は、単一のコレクションを検索する関数です。

_function findany(collection, keyword) {
    var query = createOR(keys(collection.getName()));
    if (query) {
        return collection.findOne(query, keyword);
    } else {
        return false;
    }
}
_

そして、最後にすべてのコレクションの検索機能。

_function searchAll(keyword) {
    var all = db.getCollectionNames();
    var results = [];
    all.forEach(function (collectionName) {
        print(collectionName);
        if (db[collectionName]) results.Push(findany(db[collectionName], keyword));
    });
    return results;
}
_

Mongoコンソールですべての関数をロードし、searchAll('any keyword')を実行するだけです。

1
Minime