Thanks to Julian Motz for gently helping to peer review this article.
uma das maiores diferenças entre as bases de dados SQL e NoSQL é a JOIN. Em bases de dados relacionais, a cláusula de junção SQL permite combinar linhas de duas ou mais tabelas usando um campo comum entre elas. Por exemplo, se você tem tabelas de books
e publishers
, você pode escrever comandos SQL como:
SELECT book.title, publisher.nameFROM bookLEFT JOIN book.publisher_id ON publisher.id;
por outras palavras, a tabela book
tem um campo publisher_id
que faz referência ao campo id
na tabela publisher
. Isto é prático, uma vez que um único editor poderia oferecer milhares de livros. Se alguma vez precisarmos atualizar os detalhes de um editor, podemos mudar um único registro. A redundância de dados é minimizada, uma vez que não precisamos repetir a informação do editor para cada livro. A técnica é conhecida como normalização.As bases de dados SQL oferecem uma série de características de Normalização e restrição para garantir a manutenção das relações.
NoSQL = no JOIN?
nem sempre …
bases de dados orientadas para documentos, tais como o MongoDB, são concebidas para armazenar dados desnormalizados. Idealmente, não deve haver nenhuma relação entre as coleções. Se forem exigidos os mesmos dados em dois ou mais documentos, estes devem ser repetidos.Isto pode ser frustrante, uma vez que há poucas situações em que você nunca precisa de dados relacionais. Felizmente, o MongoDB 3.2 introduz um novo operador $lookup
que pode realizar uma operação de união à esquerda em duas ou mais coleções. Mas há uma captura …
a agregação MongoDB
$lookup
só é permitida em operações de agregação. Pense nisto como um pipeline de operadores que consultam, filtram e agrupam um resultado. A saída de um operador é usada como a entrada para o próximo.
a agregação é mais difícil de compreender do que as consultas mais simples find
e irá geralmente correr mais lentamente. No entanto, eles são poderosos e uma opção inestimável para operações de busca complexas.A agregação é melhor explicada com um exemplo. Presumimos que estamos a criar uma plataforma de redes sociais com uma colecção user
. Armazena os detalhes de cada usuário em documentos separados. Por exemplo:
{ "_id": ObjectID("45b83bda421238c76f5c1969"), "name": "User One", "email: "[email protected]", "country": "UK", "dob": ISODate("1999-09-13T00:00:00.000Z")}
podemos adicionar tantos campos quanto necessário, mas todos os documentos MongoDB exigem um campo _id
que tem um valor único. A _id
é semelhante a uma chave primária SQL, e será inserida automaticamente se necessário.
nossa rede social agora requer uma coleção post
, que armazena inúmeras atualizações perspicazes dos usuários. Os documentos armazenam o texto, a data, uma classificação e uma referência ao usuário que o escreveu em um campo user_id
:
{ "_id": ObjectID("17c9812acff9ac0bba018cc1"), "user_id": ObjectID("45b83bda421238c76f5c1969"), "date: ISODate("2016-09-05T03:05:00.123Z"), "text": "My life story so far", "rating": "important"}
queremos agora mostrar os últimos vinte posts com uma classificação” importante ” de todos os usuários em ordem cronológica inversa. Cada documento devolvido deve conter o texto, a hora do post e o nome e país do usuário associado.
a consulta agregada MongoDB é aprovada por uma série de operadores de oleodutos que definem cada operação em ordem. Primeiro, precisamos extrair todos os documentos da coleção post
que têm a classificação correta usando o filtro $match
:
{ "$match": { "rating": "important" } }
agora Temos de classificar a correspondência de itens para inverter a ordem de data usando o $sort
operador:
{ "$sort": { "date": -1 } }
Desde que requerem apenas vinte posts, podemos aplicar um $limit
palco para o MongoDB só precisa processar os dados que deseja:
{ "$limit": 20 }
podemos agora juntar-se a dados do user
coleção usando o novo $lookup
operador. Exige um objeto com quatro parâmetros:
-
localField
: o campo de pesquisa no documento de entrada -
from
: a coleção para participar -
foreignField
: o campo de pesquisa nafrom
collection -
as
: o nome do campo de saída.O nosso operador é, portanto,:{ "$lookup": { "localField": "user_id", "from": "user", "foreignField": "_id", "as": "userinfo"} }
Isto irá criar um novo campo na nossa saída chamado
userinfo
. Ele contém um array onde cada valor é o correspondente ao documentouser
:"userinfo":
temos uma relação de um para um entre os
post.user_id
euser._id
, uma vez que um post só pode ter um autor. Portanto, nosso arrayuserinfo
conterá apenas um item. Podemos usar o operador$unwind
para desconstruí – lo em um sub-documento:{ "$unwind": "$userinfo" }
A saída agora será convertido para um mais prático formato que pode ter mais operadores aplicada:
"userinfo": { "name": "User One", "email: "[email protected]", …}
Finalmente, pode-se voltar o texto, o tempo do post, o nome do usuário e do país usando um
$project
estágio do pipeline:{ "$project": { "text": 1, "date": 1, "userinfo.name": 1, "userinfo.country": 1} }
juntando Tudo
Nossa última consulta agregada corresponde posts, classifica em ordem, limites para as últimas vinte itens, junta-se os dados do usuário, achata o usuário matriz e retorna campos necessários apenas. O comando completo:
db.post.aggregate();
o resultado é uma coleção de até vinte documentos. Por exemplo:
óptimo! Finalmente posso mudar para NoSQL!
MongoDB
$lookup
é útil e poderoso, mas mesmo este exemplo básico requer uma consulta agregada complexa. Não é um substituto para a cláusula de adesão mais poderosa oferecida no SQL. Nem MongoDB oferece restrições; se um documentouser
for suprimido, os documentos órfãospost
permanecerão.Idealmente, o operador de$lookup
deve ser requerido com pouca frequência. Se você precisa muito, você está possivelmente usando o armazenamento de dados errado…se você tem dados relacionais, use uma base de dados relacional (SQL)!
dito isto,
$lookup
é uma adição bem-vinda ao MongoDB 3.2. Ele pode superar algumas das questões mais frustrantes ao usar pequenas quantidades de dados relacionais em um banco de dados NoSQL.