我知道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的用户的文档,所以这就是我把它带到这里的原因。
这个答案已经过时了。从版本3.2开始,MongoDB对$lookup聚合运算符的左外连接的支持有限
MongoDB不会执行跨多个集合的查询 - 句点。当您需要连接来自多个集合的数据时,您必须通过执行多个查询在应用程序级别上执行此操作。
必须这样做应该是例外而不是常态。当您经常需要模拟这样的JOIN时,它或者意味着您在设计数据库模式时仍然认为太关系,或者您的数据根本不适合MongoDB的基于文档的存储概念。
如果对模式设计采用非规范化方法,您会发现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时可能对您有所帮助。
这是一个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
所以现在可以在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" } }
])
基于@ 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')
即可