在MongoDB中搜索多个集合
我知道MongoDB的理论和is不支持连接的事实,我应该尽可能多地使用嵌入文档或反规范化,但下面是: 我有多个文档,例如:在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
- 用户,其中嵌入郊区,但也有:名字,姓氏
- 郊区,其中嵌入了州
- 嵌入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' }
我需要执行的搜索位于:
- 用户和子用户的名字、姓氏
- 来自用户的状态
必须这样做应该是一种例外,而不是常态。当您经常需要模拟这样的连接时,这要么意味着您在设计数据库模式时仍然考虑太多的关系,要么意味着您的数据根本不适合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作为辅助键