MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Свързване и създаване на MongoDB Joins с помощта на SQL:Част 3

Множество JOINS в една заявка

Множеството JOINS обикновено се свързват с множество колекции, но трябва да имате основно разбиране за това как работи INNER JOIN (вижте предишните ми публикации по тази тема). В допълнение към нашите две колекции, които имахме преди; единици и ученици, нека добавим трета колекция и да я обозначим като спорт. Попълнете спортната колекция с данните по-долу:

{
    "_id" : 1,"tournamentsPlayed" : 6,
    "gamesParticipated" : [{"hockey" : "midfielder","football" : "stricker","handball" : "goalkeeper"}],
    "sportPlaces" : ["Stafford Bridge","South Africa", "Rio Brazil"]
}
{
    "_id" : 2,"tournamentsPlayed" : 3,
    "gamesParticipated" : [{"hockey" : "goalkeeper","football" : "stricker", "handball" : "midfielder"}],
    "sportPlaces" : ["Ukraine","India", "Argentina"]
}
{
    "_id" : 3,"tournamentsPlayed" : 10,
    "gamesParticipated" : [{"hockey" : "stricker","football" : "goalkeeper","tabletennis" : "doublePlayer"}],
    "sportPlaces" : ["China","Korea","France"]
}

Бихме искали например да върнем всички данни за ученик със стойност на полето _id, равна на 1. Обикновено ще напишем заявка за извличане на стойността на полето _id от колекцията на студентите, след което ще използваме върнатата стойност за заявка за данни в другите две колекции. Следователно това няма да е най-добрият вариант, особено ако има голям набор от документи. По-добър подход би бил да се използва SQL функцията на програмата Studio3T. Можем да запитаме нашия MongoDB с нормалната SQL концепция и след това да се опитаме да настроим грубо получения код на обвивката на Mongo, за да отговаря на нашата спецификация. Например, нека извлечем всички данни с _id, равен на 1 от всички колекции:

SELECT  *
  FROM students
    INNER JOIN units
      ON students._id = units._id
    INNER JOIN sports
      ON students._id = sports._id
  WHERE students._id = 1;

Полученият документ ще бъде:

{ 
    "students" : {"_id" : NumberInt(1),"name" : "James Washington","age" : 15.0,"grade" : "A","score" : 10.5}, 
    "units" : {"_id" : NumberInt(1),"grades" : {Maths" : "A","English" : "A","Science" : "A","History" : "B"}
    }, 
    "sports" : {
        "_id" : NumberInt(1),"tournamentsPlayed" : NumberInt(6), 
        "gamesParticipated" : [{"hockey" : "midfielder", "football" : "striker","handball" : "goalkeeper"}], 
        "sportPlaces" : ["Stafford Bridge","South Africa","Rio Brazil"]
    }
}

От раздела Код на заявка съответният код на MongoDB ще бъде:

db.getCollection("students").aggregate(
    [{ "$project" : {"_id" : NumberInt(0),"students" : "$$ROOT"}}, 
        { "$lookup" : {"localField" : "students._id","from" : "units","foreignField" : "_id", "as" : "units"}}, 
        { "$unwind" : {"path" : "$units","preserveNullAndEmptyArrays" : false}}, 
        { "$lookup" : {"localField" : "students._id","from" : "sports", "foreignField" : "_id","as" : "sports"}}, 
        { "$unwind" : {"path" : "$sports", "preserveNullAndEmptyArrays" : false}}, 
        { "$match" : {"students._id" : NumberLong(1)}}
    ]
);

Разглеждайки върнатия документ, лично аз не съм много доволен от структурата на данните, особено с вградените документи. Както можете да видите, има върнати полета _id и за единиците може да не ни е необходимо полето за оценки да бъде вградено вътре в единиците.

Бихме искали да имаме поле за единици с вградени единици, а не каквито и да било други полета. Това ни води до частта с груба мелодия. Както в предишните публикации, копирайте кода, като използвате предоставената икона за копиране и отидете в панела за обобщаване, поставете съдържанието с помощта на иконата за поставяне.

Първо, операторът $match трябва да бъде първият етап, така че го преместете на първа позиция и имайте нещо подобно:

Щракнете върху раздела на първия етап и променете заявката на:

{
    "_id" : NumberLong(1)
}

След това трябва да модифицираме допълнително заявката, за да премахнем много етапи на вграждане на нашите данни. За целта добавяме нови полета за улавяне на данни за полетата, които искаме да елиминираме, т.е.:

db.getCollection("students").aggregate(
    [
        { "$project" : { "_id" : NumberInt(0), "students" : "$$ROOT"}}, 
        { "$match" : {"students._id" : NumberLong(1)}}, 
        { "$lookup" : { "localField" : "students._id", "from" : "units","foreignField" : "_id", "as" : "units"}}, 
        { "$addFields" : { "_id": "$students._id","units" : "$units.grades"}}, 
        { "$unwind" : { "path" : "$units",  "preserveNullAndEmptyArrays" : false}}, 
        { "$lookup" : {"localField" : "students._id", "from" : "sports", "foreignField" : "_id", "as" : "sports"}}, 
        { "$unwind" : { "path" : "$sports","preserveNullAndEmptyArrays" : false}}, 
        { "$project" : {"sports._id" : 0.0}}
        ]
);

Както можете да видите, в процеса на фина настройка ние въведохме нови полеви единици, които ще презапишат съдържанието на предишния конвейер за агрегиране с оценки като вградено поле. Освен това направихме поле _id, за да посочим, че данните са във връзка с всички документи в колекциите със същата стойност. Последният етап на $project е да премахнем полето _id в спортния документ, така че да имаме изрядно представени данни, както е по-долу.

{  "_id" : NumberInt(1), 
    "students" : {"name" : "James Washington", "age" : 15.0,  "grade" : "A", "score" : 10.5}, 
    "units" : {"Maths" : "A","English" : "A", "Science" : "A","History" : "B"}, 
    "sports" : {
        "tournamentsPlayed" : NumberInt(6), 
        "gamesParticipated" : [{"hockey" : "midfielder","football" : "striker","handball" : "goalkeeper"}],  
        "sportPlaces" : ["Stafford Bridge", "South Africa", "Rio Brazil"]
        }
}

Можем също да ограничим кои полета трябва да се връщат от гледна точка на SQL. Например можем да върнем името на ученика, единиците, които този ученик прави и броя на изиграните турнири с помощта на множество JOINS с кода по-долу:

SELECT  students.name, units.grades, sports.tournamentsPlayed
  FROM students
    INNER JOIN units
      ON students._id = units._id
    INNER JOIN sports
      ON students._id = sports._id
  WHERE students._id = 1;

Това не ни дава най-подходящия резултат. Така че, както обикновено, копирайте го и го поставете в панела за агрегиране. Настройваме фино с кода по-долу, за да получим подходящия резултат.

db.getCollection("students").aggregate(
    [
        { "$project" : { "_id" : NumberInt(0), "students" : "$$ROOT"}}, 
        { "$match" : {"students._id" : NumberLong(1)}}, 
        { "$lookup" : { "localField" : "students._id", "from" : "units","foreignField" : "_id", "as" : "units"}}, 
        { "$addFields" : {"units" : "$units.grades"}}, 
        { "$unwind" : { "path" : "$units",  "preserveNullAndEmptyArrays" : false}}, 
        { "$lookup" : {"localField" : "students._id", "from" : "sports", "foreignField" : "_id", "as" : "sports"}}, 
        { "$unwind" : { "path" : "$sports","preserveNullAndEmptyArrays" : false}}, 
        { "$project" : {"name" : "$students.name", "grades" : "$units.grades", "tournamentsPlayed" : "$sports.tournamentsPlayed"}
        }}
        ]
);

Този резултат от агрегирането от концепцията SQL JOIN ни дава чиста и представителна структура от данни, показана по-долу.

{ 
    "name" : "James Washington", 
    "grades" : {"Maths" : "A", "English" : "A", "Science" : "A", "History" : "B"}, 
    "tournamentsPlayed" : NumberInt(6)
}

Доста просто, нали? Данните са доста представителни, сякаш са съхранени в една колекция като един документ.

ЛЯВО ВЪНШНО ПРИЕДИНЕНИЕ

LEFT OUTER JOIN обикновено се използва за показване на документи, които не отговарят на най-изобразяваната връзка. Полученият набор от LEFT OUTER присъединяване съдържа всички редове от двете колекции, които отговарят на критериите за клауза WHERE, както и набор от резултати INNER JOIN. Освен това всички документи от лявата колекция, които нямат съвпадащи документи в дясната колекция, също ще бъдат включени в набора от резултати. Полетата, избрани от дясната таблица, ще върнат стойности NULL. Въпреки това, всички документи в дясната колекция, които нямат съвпадащи критерии от лявата колекция, не се връщат.

Разгледайте тези две колекции:

ученици

{"_id" : 1,"name" : "James Washington","age" : 15.0,"grade" : "A","score" : 10.5}
{"_id" : 2,"name" : "Clinton Ariango","age" : 14.0,"grade" : "B","score" : 7.5}
{"_id" : 4,"name" : "Mary Muthoni","age" : 16.0,"grade" : "A","score" : 11.5}

Единици

{"_id" : 1,"Maths" : "A","English" : "A","Science" : "A","History" : "B"}
{"_id" : 2,"Maths" : "B","English" : "B","Science" : "A","History" : "B"}
{"_id" : 3,"Maths" : "A","English" : "A","Science" : "A","History" : "A"}

В колекцията от студенти нямаме стойност на полето _id, зададена на 3, но в колекцията от единици, която имаме. По същия начин в колекцията от единици няма стойност на полето _id 4. Ако използваме студентската колекция като лява опция в подхода JOIN със заявката по-долу:

SELECT *
  FROM students
    LEFT OUTER JOIN units
      ON students._id = units._id

С този код ще получим следния резултат:

{
    "students" : {"_id" : 1,"name" : "James Washington","age" : 15,"grade" : "A","score" : 10.5},
    "units" : {"_id" : 1,"grades" : {"Maths" : "A","English" : "A", "Science" : "A","History" : "B"}}
}
{
    "students" : {"_id" : 2,"name" : "Clinton Ariango", "age" : 14,"grade" : "B", "score" : 7.5 }
}
{
    "students" : {"_id" : 3,"name" : "Mary Muthoni","age" : 16,"grade" : "A","score" : 11.5},
    "units" : {"_id" : 3,"grades" : {"Maths" : "A","English" : "A","Science" : "A","History" : "A"}}
}

Вторият документ няма полето за единици, тъй като в колекцията от единици няма съответстващ документ. За тази SQL заявка съответният Mongo код ще бъде

db.getCollection("students").aggregate(
    [
        { 
            "$project" : {"_id" : NumberInt(0), "students" : "$$ROOT"}}, 
        { 
            "$lookup" : {"localField" : "students._id",  "from" : "units", "foreignField" : "_id", "as" : "units"}
        }, 
        { 
            "$unwind" : { "path" : "$units", "preserveNullAndEmptyArrays" : true}
        }
    ]
);

Разбира се, научихме за фината настройка, така че можете да продължите напред и да преструктурирате конвейера за агрегация, за да отговаряте на крайния резултат, който искате. SQL е много мощен инструмент, що се отнася до управлението на база данни. Това е широка тема сама по себе си, можете също да опитате да използвате клаузите IN и GROUP BY, за да получите съответния код за MongoDB и да видите как работи.

Заключение

Свикването с нова технология (база данни) в допълнение към тази, с която сте свикнали да работите, може да отнеме много време. Релационните бази данни все още са по-често срещани от нерелационните. Независимо от това, с въвеждането на MongoDB нещата се промениха и хората биха искали да го научат възможно най-бързо поради свързаната с него мощна производителност.

Изучаването на MongoDB от нулата може да бъде малко досадно, но можем да използваме познанията по SQL, за да манипулираме данни в MongoDB, да получим относителния код на MongoDB и да го настроим фино, за да получим най-подходящите резултати. Един от наличните инструменти за подобряване на това е Studio 3T. Той предлага две важни функции, които улесняват работата със сложни данни, а именно:функция за SQL заявка и редактор за агрегиране. Заявките за фина настройка не само ще ви гарантират най-добрия резултат, но и ще подобрят производителността по отношение на спестяване на време.


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Съвпадение с подниз в агрегирането на mongodb

  2. MongoDB $radiansToDegrees

  3. Данните за актуализиране на MongoDB във вложено поле

  4. много към много отношения с nosql (mongodb и mongoose)

  5. MongoDB $ или оператор на конвейер за агрегиране