MongoDB/CouchDB: Присъединете таблица към себе си

Имам модел на релационни данни, който обмислям да мигрирам към MongoDB или CouchDB, и се опитвам да разбера как ще работят заявките. Да предположим, че имам два обекта, Служители и Проекти, и таблица за свързване много към много, наречена Задания. Искам да направя заявка за всички проекти, по които двама потребители са си сътрудничили. В SQL мога да направя нещо подобно:

SELECT DISTINCT a1.project_id
FROM assignments a1, assignments a2
WHERE a1.project_id = a2.project_id
AND a1.employee_id = ?
AND a2.employee_id = ?

Как бих направил това в NoSQL, ако приемем, че имам „документи“ за служител, проект и назначение? Или бихте структурирали документите по различен начин и как това ще се отрази на заявката?

Ще се радвам да чуя отговори както за приложния програмен интерфейс (API) за заявки на Mongo, така и за подхода за карта/намаляване на Couch.


person Paul A Jungwirth    schedule 29.05.2011    source източник


Отговори (3)


Мога само да споделя гледката от дивана, където от време на време си почивам. Първо, почти винаги трябва умишлено да забравите SQL, когато работите със системи, базирани на документи.

Второ, ще имате възможност или да промените структурата и да намалите броя на обектите до минимум, който няма да изисква обединения, или можете да използвате изгледи, които ще комбинират документи от различни типове в един резултатен набор.

Първият (редизайн) е предпочитаният метод, тъй като съединенията са по същество чужд подход за nosql, тъй като нормализирането не е изискване там. Базираното на документи не е релационно.

person Dennis Kreminsky    schedule 29.05.2011
comment
Добре, благодаря за отговора. Но имам проблем да разбера модел на документ, който ще ми позволи да направя заявка за този вид структура. Някакви идеи? - person Paul A Jungwirth; 30.05.2011
comment
Вярвам, че ще трябва да изберете основния обект, не мога да помогна с това, защото не знам нищо за вашето приложение. Ако центрирате системата си около потребителите и проектите са сравнително прости, обвийте проектите в потребителите или обратното. Ако потребителите и проектите са еднакво важни, включете част от данните на потребителя в документа за проект/задание, така че да е достатъчно за показване на всичко, което вашият пример за присъединяване е извлякъл (с недостатъка, че е необходимо да актуализирате два документа, ако някой се промени). И накрая, можете да включите потребителски идентификатори в проекти и това ще бъде присъединяване. - person Dennis Kreminsky; 30.05.2011
comment
PS назначенията най-вероятно ще трябва да се слеят или в потребители, или в проекти, в зависимост от вашето приложение. - person Dennis Kreminsky; 30.05.2011

В MongoDB можете да го направите така. Използвам интерактивния JavaScript Shell.

Първо създайте няколко потребители:

> db.so.employee.insert({name: "Joe"})
> db.so.employee.insert({name: "Moe"})
> db.so.employee.insert({name: "Bart"})
> db.so.employee.insert({name: "Homer"})
> db.so.employee.find()
{ "_id" : ObjectId("4de35ccbcc0379536e1ac43b"), "name" : "Joe" }
{ "_id" : ObjectId("4de35ccfcc0379536e1ac43c"), "name" : "Moe" }
{ "_id" : ObjectId("4de35cd3cc0379536e1ac43d"), "name" : "Bart" }
{ "_id" : ObjectId("4de35cd7cc0379536e1ac43e"), "name" : "Homer" }

Сега създайте няколко проекта

> db.so.project.insert({name: "Web App A"})
> db.so.project.insert({name: "Web App B"})
> db.so.project.insert({name: "Web App C"})
> db.so.project.find();
{ "_id" : ObjectId("4de35d0fcc0379536e1ac43f"), "name" : "Web App A" }
{ "_id" : ObjectId("4de35d13cc0379536e1ac440"), "name" : "Web App B" }
{ "_id" : ObjectId("4de35d15cc0379536e1ac441"), "name" : "Web App C" }

Добавете потребителите към проектите

> db.so.project.update({name: "Web App A"}, {$push: {employees: ObjectId('4de35ccbcc0379536e1ac43b') }})
> db.so.project.update({name: "Web App A"}, {$push: {employees: ObjectId('4de35ccfcc0379536e1ac43c') }})
> db.so.project.update({name: "Web App B"}, {$push: {employees: ObjectId('4de35ccfcc0379536e1ac43c') }})
> db.so.project.update({name: "Web App C"}, {$push: {employees: ObjectId('4de35ccfcc0379536e1ac43c') }})
> db.so.project.update({name: "Web App B"}, {$push: {employees: ObjectId('4de35cd3cc0379536e1ac43d') }})
> db.so.project.update({name: "Web App C"}, {$push: {employees: ObjectId('4de35cd3cc0379536e1ac43d') }})
> db.so.project.update({name: "Web App B"}, {$push: {employees: ObjectId('4de35cd7cc0379536e1ac43e') }})

> db.so.project.find()
{ "_id" : ObjectId("4de35d0fcc0379536e1ac43f"), "employees" : [
    ObjectId("4de35ccbcc0379536e1ac43b"),
    ObjectId("4de35ccfcc0379536e1ac43c")
], "name" : "Web App A" }
{ "_id" : ObjectId("4de35d15cc0379536e1ac441"), "employees" : [
    ObjectId("4de35ccfcc0379536e1ac43c"),
    ObjectId("4de35cd3cc0379536e1ac43d")
], "name" : "Web App C" }
{ "_id" : ObjectId("4de35d13cc0379536e1ac440"), "employees" : [
    ObjectId("4de35cd3cc0379536e1ac43d"),
    ObjectId("4de35cd7cc0379536e1ac43e")
], "name" : "Web App B" }

Ако сега искате да намерите всички проекти, по които работи "Joe"

> db.so.project.find({employees: ObjectId('4de35ccbcc0379536e1ac43b') }, {name: 1})
{ "_id" : ObjectId("4de35d0fcc0379536e1ac43f"), "name" : "Web App A" }

Намерете всички проекти, по които работи Джо ИЛИ Мо

> db.so.project.find({employees: {$in: [ObjectId('4de35ccbcc0379536e1ac43b'), ObjectId('4de35ccfcc0379536e1ac43c')] }}, {name: 1})
{ "_id" : ObjectId("4de35d0fcc0379536e1ac43f"), "name" : "Web App A" }
{ "_id" : ObjectId("4de35d15cc0379536e1ac441"), "name" : "Web App C" }
{ "_id" : ObjectId("4de35d13cc0379536e1ac440"), "name" : "Web App B" }

Намерете всички проекти, по които работят Джо И Моу

> db.so.project.find({employees: {$all: [ObjectId('4de35ccbcc0379536e1ac43b'), ObjectId('4de35ccfcc0379536e1ac43c')] }}, {name: 1})
{ "_id" : ObjectId("4de35d0fcc0379536e1ac43f"), "name" : "Web App A" }

За да получите всички служители за конкретен проект, имате нужда от две заявки. Намиране на всички служители от проект C.

> db.so.project.find({name: "Web App C"}, {employees: 1})
{ "_id" : ObjectId("4de35d15cc0379536e1ac441"), "employees" : [
    ObjectId("4de35ccfcc0379536e1ac43c"),
    ObjectId("4de35cd3cc0379536e1ac43d")
] }

Във вашето приложение създавате нова заявка от върнатите стойности и създавате тази заявка:

> db.so.employee.find({_id: {$in: [ObjectId('4de35ccfcc0379536e1ac43c'), ObjectId('4de35cd3cc0379536e1ac43d')] }})
{ "_id" : ObjectId("4de35ccfcc0379536e1ac43c"), "name" : "Moe" }
{ "_id" : ObjectId("4de35cd3cc0379536e1ac43d"), "name" : "Bart" }

Надявах се, че това помогна да се разбере как работи MongoDB и как можете да изградите отношения. Тук използвах ръчно дерефериране. Това означава, че запазвам ObjectID директно и го извличам ръчно. Съществува и "DBRef" и вашият драйвер го извлича вместо вас.

person David Raab    schedule 30.05.2011
comment
Има ли начин да се присъединят служителите в заявката за проекта, така че да можете да комбинирате последните две заявки в една? - person FrederickCook; 30.07.2011
comment
Не, MongoDB няма обединения, това е функция. Трябва да го направите на клиента. Във вашия софтуер. - person David Raab; 31.07.2011
comment
Отлични коментари Сид, наистина полезни - person Rob Boerman; 22.09.2011

Това, което търсите, е ефективно N:M картографиране. В този случай вие се опитвате да картографирате таблица към себе си, но това не е много по-различно от опитите да картографирате „Служители“ към „Проекти“.

Има дълъг отговор тук на SO че няма да повтарям тук.

Във вашия конкретен случай мисля, че трябва да пренастроите данните си малко. Имате таблица assignments, съдържаща две точки от данни: projectID и employeedID. Това е класическа таблица за свързване на N проекта към M служители.

В MongoDB обикновено изобщо нямате тази „таблица“. Ако искате да назначите служители към проект, можете просто да съхраните масив от employeeID в самия проект.

{ name: 'projectx', emps: [ 1, 2, 3] }
{ name: 'projecty', emps: [ 3, 2] }

Изглежда, че вашата заявка е основно "намерете всички проекти, в които служител 2 и 3 работят заедно". MongoDB има $all оператор на заявка, който ще направи това за вие с горната структура.

Моля, имайте предвид, че това е само едно запитване. Важно е да разгледате цялата си система и да идентифицирате всички важни заявки, когато правите дизайн на схема.

person Gates VP    schedule 30.05.2011