MongoDB: Combine data from multiple collections into one..how?

后端 未结 11 1261
余生分开走
余生分开走 2020-11-22 06:44

How can I (in MongoDB) combine data from multiple collections into one collection?

Can I use map-reduce and if so then how?

I would greatly appreciate some

11条回答
  •  不要未来只要你来
    2020-11-22 07:31

    Doing unions in MongoDB in a 'SQL UNION' fashion is possible using aggregations along with lookups, in a single query. Here is an example I have tested that works with MongoDB 4.0:

    // Create employees data for testing the union.
    db.getCollection('employees').insert({ name: "John", type: "employee", department: "sales" });
    db.getCollection('employees').insert({ name: "Martha", type: "employee", department: "accounting" });
    db.getCollection('employees').insert({ name: "Amy", type: "employee", department: "warehouse" });
    db.getCollection('employees').insert({ name: "Mike", type: "employee", department: "warehouse"  });
    
    // Create freelancers data for testing the union.
    db.getCollection('freelancers').insert({ name: "Stephany", type: "freelancer", department: "accounting" });
    db.getCollection('freelancers').insert({ name: "Martin", type: "freelancer", department: "sales" });
    db.getCollection('freelancers').insert({ name: "Doug", type: "freelancer", department: "warehouse"  });
    db.getCollection('freelancers').insert({ name: "Brenda", type: "freelancer", department: "sales"  });
    
    // Here we do a union of the employees and freelancers using a single aggregation query.
    db.getCollection('freelancers').aggregate( // 1. Use any collection containing at least one document.
      [
        { $limit: 1 }, // 2. Keep only one document of the collection.
        { $project: { _id: '$$REMOVE' } }, // 3. Remove everything from the document.
    
        // 4. Lookup collections to union together.
        { $lookup: { from: 'employees', pipeline: [{ $match: { department: 'sales' } }], as: 'employees' } },
        { $lookup: { from: 'freelancers', pipeline: [{ $match: { department: 'sales' } }], as: 'freelancers' } },
    
        // 5. Union the collections together with a projection.
        { $project: { union: { $concatArrays: ["$employees", "$freelancers"] } } },
    
        // 6. Unwind and replace root so you end up with a result set.
        { $unwind: '$union' },
        { $replaceRoot: { newRoot: '$union' } }
      ]);
    

    Here is the explanation of how it works:

    1. Instantiate an aggregate out of any collection of your database that has at least one document in it. If you can't guarantee any collection of your database will not be empty, you can workaround this issue by creating in your database some sort of 'dummy' collection containing a single empty document in it that will be there specifically for doing union queries.

    2. Make the first stage of your pipeline to be { $limit: 1 }. This will strip all the documents of the collection except the first one.

    3. Strip all the fields of the remaining document by using a $project stage:

      { $project: { _id: '$$REMOVE' } }
      
    4. Your aggregate now contains a single, empty document. It's time to add lookups for each collection you want to union together. You may use the pipeline field to do some specific filtering, or leave localField and foreignField as null to match the whole collection.

      { $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
      { $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
      { $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
      
    5. You now have an aggregate containing a single document that contains 3 arrays like this:

      {
          Collection1: [...],
          Collection2: [...],
          Collection3: [...]
      }
      

      You can then merge them together into a single array using a $project stage along with the $concatArrays aggregation operator:

      {
        "$project" :
        {
          "Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
        }
      }
      
    6. You now have an aggregate containing a single document, into which is located an array that contains your union of collections. What remains to be done is to add an $unwind and a $replaceRoot stage to split your array into separate documents:

      { $unwind: "$Union" },
      { $replaceRoot: { newRoot: "$Union" } }
      
    7. Voilà. You now have a result set containing the collections you wanted to union together. You can then add more stages to filter it further, sort it, apply skip() and limit(). Pretty much anything you want.

提交回复
热议问题