我知道MongoDB的理论以及不支持连接的事实,我应该尽可能地使用嵌入文档或非规范化,但是这里有:
我有多个文档,例如:
示例:
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的用户那里得到文档,所以这就是我把它带到这里的原因。
答案 0 :(得分:35)
这个答案已经过时了。从版本3.2开始,MongoDB对使用$lookup聚合运算符
的左外连接的支持有限
MongoDB不会执行跨多个集合的查询 - 句点。当您需要连接来自多个集合的数据时,您必须通过执行多个查询在应用程序级别上执行此操作。
必须这样做应该是例外而不是常态。当您经常需要模拟这样的JOIN时,它或者意味着您在设计数据库模式时仍然认为太关系,或者您的数据不适合MongoDB的基于文档的存储概念。
答案 1 :(得分: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时可能对您有所帮助的资源。
答案 2 :(得分: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}) >伦理委员会
答案 3 :(得分: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" } }
])
答案 4 :(得分: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')
答案 5 :(得分:0)
您可以使用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 }
])
以下操作首先使用$ lookup阶段通过item字段将两个集合合并,然后在$ 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 } }
])
该操作返回以下文档:
{ "_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 }
此技术合并对象并返回结果
答案 6 :(得分:0)
Minimum解决方案有效,但需要修复: var query = createOR(keys(collection.getName())); 需要将关键字作为第二个参数添加到此处的createOR调用。