Подтвердить что ты не робот

Поиск по нескольким коллекциям в MongoDB

Я знаю теорию MongoDB и тот факт, что это не поддерживает объединения, и что я должен использовать встроенные документы или денормализовать как можно больше, но здесь идет:

У меня есть несколько документов, например:

  • Пользователи, которые внедряют Suburbs, но также имеют: имя, фамилию
  • Пригороды, в которые вставляются государства
  • Ребенок, который вставляет School, принадлежит Пользователю, но также имеет: имя, фамилию

Пример:

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, поэтому я его здесь привел.

4b9b3361

Ответ 1

Этот ответ устарел. Начиная с версии 3.2, MongoDB имеет ограниченную поддержку левых внешних объединений с оператором агрегации $lookup

MongoDB не выполняет запросы, которые охватывают несколько коллекций - период. Когда вам нужно объединить данные из нескольких коллекций, вы должны сделать это на уровне приложения, выполнив несколько запросов.

  • Коллекция запросов A
  • Получить вторичные ключи из результата и поместить их в массив
  • Коллекция запросов B, передающая этот массив как значение $in-operator
  • Программно объединить результаты обоих запросов на уровне приложений

Необходимость этого должна быть скорее исключением, чем нормой. Когда вам часто нужно эмулировать JOINs, как это, это означает, что вы все еще думаете слишком реляционно, когда вы разрабатываете схему базы данных или что ваши данные просто не подходят для концепции хранения на основе документов MongoDB.

Ответ 2

Вы найдете 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.

Ответ 3

Здесь функция 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 вы можете скопировать/вставить функцию, а затем вызвать ее так:

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

Ответ 4

Итак, теперь объединение возможно в 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" } }
])

Ответ 5

На основе @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')