MongoDB запрашивает сразу несколько коллекций
users
{
"_id":"12345",
"admin":1
},
{
"_id":"123456789",
"admin":0
}
posts
{
"content":"Some content",
"owner_id":"12345",
"via":"facebook"
},
{
"content":"Some other content",
"owner_id":"123456789",
"via":"facebook"
}
Вот пример моего монгоба. Я хочу получить все сообщения с атрибутом "через", равным "facebook" и отправленным администратором ( "admin": 1). Я не мог понять, как получить этот запрос. Поскольку mongodb не является реляционной базой данных, я не мог выполнить операцию соединения. Каким может быть решение?
Ответы
Ответ 1
Пытаясь ПРИСОЕДИНЯТЬСЯ в MongoDB, победит цель использования MongoDB. Однако вы можете использовать DBref и написать свой код (или библиотеку) на уровне приложения, чтобы он автоматически извлекал эти ссылки для вас.
Или вы можете изменить свою схему и использовать встроенные документы.
Ваш последний выбор - оставить вещи точно так, как они есть сейчас, и сделать два запроса.
Ответ 2
Вот ответ на ваш вопрос.
db.getCollection('users').aggregate([
{$match : {admin : 1}},
{$lookup: {from: "posts",localField: "_id",foreignField: "owner_id",as: "posts"}},
{$project : {
posts : { $filter : {input : "$posts" , as : "post", cond : { $eq : ['$$post.via' , 'facebook'] } } },
admin : 1
}}
])
Или вы можете пойти с вариантом группы mongodb.
db.getCollection('users').aggregate([
{$match : {admin : 1}},
{$lookup: {from: "posts",localField: "_id",foreignField: "owner_id",as: "posts"}},
{$unwind : "$posts"},
{$match : {"posts.via":"facebook"}},
{ $group : {
_id : "$_id",
posts : {$push : "$posts"}
}}
])
Ответ 3
Как упоминалось ранее в MongoDB, вы не можете ОБЪЕДИНЯТЬ между коллекциями.
Для вашего примера решение может быть:
var myCursor = db.users.find({admin:1});
var user_id = myCursor.hasNext() ? myCursor.next() : null;
db.posts.find({owner_id : user_id._id});
См. справочное руководство - раздел курсоров: http://es.docs.mongodb.org/manual/core/cursors/
Другим решением было бы включить пользователей в сбор сообщений, но я думаю, что для большинства веб-приложений сбор пользователей должен быть независимым по соображениям безопасности. В коллекции пользователей могут быть роли, разрешения и т.д.
posts
{
"content":"Some content",
"user":{"_id":"12345", "admin":1},
"via":"facebook"
},
{
"content":"Some other content",
"user":{"_id":"123456789", "admin":0},
"via":"facebook"
}
а затем:
db.posts.find({user.admin: 1 });
Ответ 4
Вы можете использовать $lookup
(multiple) для получения записей из нескольких коллекций:
Пример:
Если у вас больше коллекций (у меня есть 3 коллекции для демонстрации здесь, у вас может быть больше 3). и я хочу получить данные из 3 коллекций в одном объекте:
Коллекция выглядит так:
db.doc1.find() довольно();.
{
"_id" : ObjectId("5901a4c63541b7d5d3293766"),
"firstName" : "shubham",
"lastName" : "verma"
}
db.doc2.find() довольно();.
{
"_id" : ObjectId("5901a5f83541b7d5d3293768"),
"userId" : ObjectId("5901a4c63541b7d5d3293766"),
"address" : "Gurgaon",
"mob" : "9876543211"
}
db.doc3.find() довольно();.
{
"_id" : ObjectId("5901b0f6d318b072ceea44fb"),
"userId" : ObjectId("5901a4c63541b7d5d3293766"),
"fbURLs" : "http://www.facebook.com",
"twitterURLs" : "http://www.twitter.com"
}
Теперь ваш запрос будет выглядеть следующим образом:
db.doc1.aggregate([
{ $match: { _id: ObjectId("5901a4c63541b7d5d3293766") } },
{
$lookup:
{
from: "doc2",
localField: "_id",
foreignField: "userId",
as: "address"
}
},
{
$unwind: "$address"
},
{
$project: {
__v: 0,
"address.__v": 0,
"address._id": 0,
"address.userId": 0,
"address.mob": 0
}
},
{
$lookup:
{
from: "doc3",
localField: "_id",
foreignField: "userId",
as: "social"
}
},
{
$unwind: "$social"
},
{
$project: {
__v: 0,
"social.__v": 0,
"social._id": 0,
"social.userId": 0
}
}
]).pretty();
Тогда ваш результат будет:
{
"_id" : ObjectId("5901a4c63541b7d5d3293766"),
"firstName" : "shubham",
"lastName" : "verma",
"address" : {
"address" : "Gurgaon"
},
"social" : {
"fbURLs" : "http://www.facebook.com",
"twitterURLs" : "http://www.twitter.com"
}
}
Если вам нужны все записи из каждой коллекции, вы должны удалить строку ниже из запроса:
{
$project: {
__v: 0,
"address.__v": 0,
"address._id": 0,
"address.userId": 0,
"address.mob": 0
}
}
{
$project: {
"social.__v": 0,
"social._id": 0,
"social.userId": 0
}
}
После удаления кода вы получите полную запись:
{
"_id" : ObjectId("5901a4c63541b7d5d3293766"),
"firstName" : "shubham",
"lastName" : "verma",
"address" : {
"_id" : ObjectId("5901a5f83541b7d5d3293768"),
"userId" : ObjectId("5901a4c63541b7d5d3293766"),
"address" : "Gurgaon",
"mob" : "9876543211"
},
"social" : {
"_id" : ObjectId("5901b0f6d318b072ceea44fb"),
"userId" : ObjectId("5901a4c63541b7d5d3293766"),
"fbURLs" : "http://www.facebook.com",
"twitterURLs" : "http://www.twitter.com"
}
}
Ответ 5
Выполните несколько запросов или используйте встроенные документы или посмотрите "ссылки на базы данных".
Ответ 6
Одно решение: добавьте флаг adAdmin: 0/1 в ваш документ после сбора.
Другое решение: используйте DBrefs
Ответ 7
Вы можете написать пример JavaScript, как показано ниже, и вызвать функцию, когда требуется.
Обратитесь к иллюстрации по адресу: http://dbversity.com/mongodb-querying-multiple-collections-with-a-javascript/
function colListQuery() {
var tcol = new Array()
tcol= db.getCollectionNames();
for(var i = 1; i < tcol.length ; i++) {
query = "db." + tcol[i] + ".find()";
var docs= eval(query);
docs.forEach( function(doc, index){ print( "Database_Name:", db, "Collection_Name:", tcol[i], "x_value:", doc.x, "_id:", doc._id) });
}
}
Затем вызовите его с помощью colListQuery(), когда вы требуете его, как показано в иллюстрации.