在MongoDB中搜索多个集合

在MongoDB中搜索多个集合,mongodb,mongodb-query,aggregation-framework,Mongodb,Mongodb Query,Aggregation Framework,我知道MongoDB的理论和is不支持连接的事实,我应该尽可能多地使用嵌入文档或反规范化,但下面是: 我有多个文档,例如: 用户,其中嵌入郊区,但也有:名字,姓氏 郊区,其中嵌入了州 嵌入School的Child属于用户,但也有:名、姓 例如: Users: { _id: 1, first_name: 'Bill', last_name: 'Gates', suburb: 1 } { _id: 2, first_name: 'Steve', last_name: 'Jobs', suburb

我知道MongoDB的理论和is不支持连接的事实,我应该尽可能多地使用嵌入文档或反规范化,但下面是:

我有多个文档,例如:

  • 用户,其中嵌入郊区,但也有:名字,姓氏
  • 郊区,其中嵌入了州
  • 嵌入School的Child属于用户,但也有:名、姓
例如:

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的文档,所以我在这里介绍了它

这个答案已经过时了。自3.2版以来,MongoDB对使用聚合运算符的左外部联接的支持有限

MongoDB不执行跨多个集合-期间的查询。当需要连接来自多个集合的数据时,必须在应用程序级别执行多个查询

  • 查询集合A
  • 从结果中获取次键并将其放入数组中
  • 查询集合B将该数组作为
  • 在应用层以编程方式连接两个查询的结果

  • 必须这样做应该是一种例外,而不是常态。当您经常需要模拟这样的连接时,这要么意味着您在设计数据库模式时仍然考虑太多的关系,要么意味着您的数据根本不适合MongoDB基于文档的存储概念。

    如果采用非规范化的模式设计方法,您会发现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 ]
    }
    

    第一个优点是这个模式更容易查询。此外,地址字段的更新现在与个人实体一致,因为这些字段嵌入到单个文档中。还要注意父母和孩子之间的双向关系吗?这使得这个集合不仅仅是个人的集合。父子关系意味着此集合也是一个集合。以下是一些资源,可能会对您的思考有所帮助。

    以下是一个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}) >记录


    基于@brian moquin和其他人,我创建了一组函数,通过简单的关键字使用整个键(字段)搜索整个集合

    这是我的要点

    为了了解更多细节,我首先创建了一个函数来收集所有键

    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');
    }
    
    然后再从keys数组生成一个
    $或
    查询

    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')

    ,因此现在可以在mongodb中加入,您可以在这里使用和聚合来实现这一点,这可能是在多个集合中查找的最佳方式

    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" } }
    ])
    

    您可以通过MongoDB驱动程序使用$mergeObjects实现这一点 例子 使用以下文档创建收款单:

    db.orders.insert([
      { "_id" : 1, "item" : "abc", "price" : 12, "ordered" : 2 },
      { "_id" : 2, "item" : "jkl", "price" : 20, "ordered" : 1 }
    ])
    
    db.items.insert([
      { "_id" : 1, "item" : "abc", description: "product 1", "instock" : 120 },
      { "_id" : 2, "item" : "def", description: "product 2", "instock" : 80 },
      { "_id" : 3, "item" : "jkl", description: "product 3", "instock" : 60 }
    ])
    
    { "_id" : 1, "item" : "abc", "description" : "product 1", "instock" : 120, "price" : 12, "ordered" : 2 }
    { "_id" : 2, "item" : "jkl", "description" : "product 3", "instock" : 60, "price" : 20, "ordered" : 1 }
    
    使用以下文档创建另一个集合项:

    db.orders.insert([
      { "_id" : 1, "item" : "abc", "price" : 12, "ordered" : 2 },
      { "_id" : 2, "item" : "jkl", "price" : 20, "ordered" : 1 }
    ])
    
    db.items.insert([
      { "_id" : 1, "item" : "abc", description: "product 1", "instock" : 120 },
      { "_id" : 2, "item" : "def", description: "product 2", "instock" : 80 },
      { "_id" : 3, "item" : "jkl", description: "product 3", "instock" : 60 }
    ])
    
    { "_id" : 1, "item" : "abc", "description" : "product 1", "instock" : 120, "price" : 12, "ordered" : 2 }
    { "_id" : 2, "item" : "jkl", "description" : "product 3", "instock" : 60, "price" : 20, "ordered" : 1 }
    
    以下操作首先使用$lookup阶段通过项字段连接两个集合,然后使用$replaceRoot中的$mergeObjects合并来自项和订单的连接文档:

    db.orders.aggregate([
       {
          $lookup: {
             from: "items",
             localField: "item",    // field in the orders collection
             foreignField: "item",  // field in the items collection
             as: "fromItems"
          }
       },
       {
          $replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$fromItems", 0 ] }, "$$ROOT" ] } }
       },
       { $project: { fromItems: 0 } }
    ])
    
    该操作将返回以下文档:

    db.orders.insert([
      { "_id" : 1, "item" : "abc", "price" : 12, "ordered" : 2 },
      { "_id" : 2, "item" : "jkl", "price" : 20, "ordered" : 1 }
    ])
    
    db.items.insert([
      { "_id" : 1, "item" : "abc", description: "product 1", "instock" : 120 },
      { "_id" : 2, "item" : "def", description: "product 2", "instock" : 80 },
      { "_id" : 3, "item" : "jkl", description: "product 3", "instock" : 60 }
    ])
    
    { "_id" : 1, "item" : "abc", "description" : "product 1", "instock" : 120, "price" : 12, "ordered" : 2 }
    { "_id" : 2, "item" : "jkl", "description" : "product 3", "instock" : 60, "price" : 20, "ordered" : 1 }
    

    此技术合并对象并返回结果

    最小解决方案有效,但需要修复: var query=createOR(key(collection.getName());
    需要将关键字作为第二个参数添加到createOR调用中。

    我认为两者都不是。聚合框架和map reduce的目标是聚合/汇总单个集合中的数据。类联接操作应由应用程序而不是数据库处理。作为旁注-你不想“尽可能地去规范化”。你能编辑这个问题,包括你试图解决这个问题的步骤吗?我不知道你被困在哪里了,我还没怎么试过。当我看到mapReduce没有用state_id填充我的用户文档时,我退出了,并寻求建议。你好,Philipp,我完全理解你的逐步解释,但我想知道的是,是否有任何方法可以直接在MongoDB中执行上述查询,通过不同的javascript函数或等效的存储过程,您可以在MongoDB数据库服务器上使用。但是,我可以使用rockmongo这样的GUI组织多个查询吗?我认为,如果youtube上10gen视频中的每个数据都有多对多关系,那么就我而言,我们的数据根本不适合基于文档的存储。但无模式设计的易用性、扩展和分发等操作的易用性以及后台索引等实用程序使我们选择了mongodb。因此,即使我们通过多个查询,我也无法将同一个文档写入2亿次,而不是仅将一个ID作为辅助键