在MongoDB中搜索多个集合

问题描述 投票:35回答:5

我知道MongoDB的理论以及不支持连接的事实,并且我应该尽可能地使用嵌入文档或非规范化,但是这里是:

我有多个文件,例如:

  • 用户,嵌入Suburbs,但也有:名字,姓氏
  • 郊区,嵌入国家
  • 嵌入学校的孩子属于用户,但也有:名字,姓氏

例:

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的用户的文档,所以这就是我把它带到这里的原因。

mongodb mongodb-query aggregation-framework
5个回答
36
投票

这个答案已经过时了。从版本3.2开始,MongoDB对$lookup聚合运算符的左外连接的支持有限

MongoDB不会执行跨多个集合的查询 - 句点。当您需要连接来自多个集合的数据时,您必须通过执行多个查询在应用程序级别上执行此操作。

  1. 查询集合A.
  2. 从结果中获取辅助密钥并将它们放入数组中
  3. 查询集合B将该数组作为$in-operator的值传递
  4. 以编程方式在应用程序层上加入两个查询的结果

必须这样做应该是例外而不是常态。当您经常需要模拟这样的JOIN时,它或者意味着您在设计数据库模式时仍然认为太关系,或者您的数据根本不适合MongoDB的基于文档的存储概念。


15
投票

如果对模式设计采用非规范化方法,您会发现MongoDB更容易理解。也就是说,您希望按照请求客户端应用程序理解它们的方式构建文档。基本上,您将文档建模为应用程序处理的domain objects。当您以这种方式建模数据时,连接变得不那么重要了。考虑一下我如何将数据反规范化为单个集合:

{  
    _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实体一致,因为字段嵌入在单个文档中。还要注意父母和孩子之间的双向关系?这使得这个系列不仅仅是个人的集合。亲子关系意味着这个集合也是social graph。以下是一些资源,在考虑schema design in MongoDB时可能对您有所帮助。


9
投票

这是一个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


5
投票

所以现在可以在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" } }
])

1
投票

基于@ 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')即可

© www.soinside.com 2019 - 2024. All rights reserved.