O Couchbase Lite 2.0 suporta a capacidade de realizar JOINS em seus documentos JSON. Isso faz parte da nova interface de consulta baseada em N1QLa linguagem de consulta declarativa do Couchbase que estende o SQL para JSON. Se você estiver familiarizado com SQL, vai se sentir em casa com a semântica da nova API.
Os JOINS permitem que você combine o conteúdo de vários documentos. Nesta publicação, forneceremos exemplos para ilustrar os tipos de JOINS possíveis com o Couchbase Lite. Para cada uma das consultas, forneceremos a consulta SQL equivalente. Este blog pressupõe que você esteja familiarizado com os fundamentos da nova API de consulta, portanto, se ainda não tiver feito isso, não deixe de revisar o artigo postagem anterior primeiro. Se estiver interessado, os links para blogs que discutem outros recursos da interface do Query são fornecidos no final desta publicação.
Você pode fazer o download das compilações mais recentes do Couchbase Mobile 2.0 Pre-Release em nosso downloads página.
Histórico
Se você estava usando as versões 1.x do Couchbase Mobile, provavelmente está familiarizado com Visualizações de mapas para criar índices e consultas. Na versão 2.0, você não precisa mais criar visualizações e funções de mapa! Em vez disso, uma interface simples permite a criação de índices e você pode usar uma interface do Query Builder para construir suas consultas. A nova interface de consulta é mais simples de usar e muito mais poderosa em comparação. Vamos descobrir alguns de seus recursos nesta postagem.
Projeto de amostra
Embora os exemplos discutidos aqui usem o Swift para iOS, a mesma interface de consulta também é compatível com as plataformas Android e Windows.
Portanto, com alguns pequenos ajustes, você poderá reutilizar os exemplos de consulta desta postagem ao trabalhar com outras plataformas.
Siga as instruções abaixo se você estiver interessado em um projeto Swift de amostra
- Clone o iOS Swift Playground do GitHub
1 |
$ git clone https://github.com/couchbaselabs/couchbase-lite-ios-api-playground |
- Siga as instruções de instalação no manual correspondente LEIAME para criar e executar o playground.
Modelo de dados de amostra
Usaremos um banco de dados de amostra localizado aqui. Você pode incorporar esse banco de dados pré-criado em seu aplicativo móvel e começar a usá-lo para suas consultas.
O conjunto de dados de amostra é um pouco artificial, mas o objetivo aqui é demonstrar alguns casos de uso típicos de unir-se
consultas.
- "Documento do tipo "funcionário
1 2 3 4 5 6 7 |
{ "tipo": "funcionário", "firstname": "John", "lastname" (sobrenome): "Smith", "departamento": "1000", "localização": "101" } |
- "Documento do tipo "departamento
1 2 3 4 5 6 7 8 9 10 11 |
{ "tipo": "departamento", "name" (nome): "Gerenciamento de produtos", "código": "2000", "cabeça": { "firstname": "Patricia", "lastname" (sobrenome): "Shoji" }, "localização":["101","102"] } |
- Documento do tipo "location" (local)
1 2 3 4 5 6 |
{ "tipo": "localização", "name" (nome): "HQ", "endereço": "1123 6th St. Melbourne, FL 32904 ", "código": "101" } |
** Consulte o modelo acima para cada um dos exemplos de consulta abaixo. **
O identificador do banco de dados
Nas consultas abaixo, usaremos o Banco de dados
API para abrir/criar o banco de dados CouchbaseLite.
1 2 |
deixar opções = DatabaseConfiguration() deixar db = tentar Banco de dados(nome: kDBName, configuração: opções) |
Índices
Para acelerar as consultas de leitura, você pode criar índices nas propriedades que serão consultadas. A melhoria no desempenho seria significativa em grandes conjuntos de dados. Obviamente, esteja ciente de que haverá um aumento na necessidade de armazenamento para guardar os índices, e o desempenho das gravações também poderá ser afetado. Portanto, tenha cuidado ao criar muitos índices.
O exemplo a seguir cria um Índice de valor
no tipo
propriedade de um documento
1 |
tentar db.createIndex(IndexBuilder.valueIndex(itens: ValueIndexItem.propriedade("tipo")),withName: "typeIndex") |
JOIN ou Inner JOIN
Você pode usar uma consulta JOIN ou Inner JOIN simples para buscar propriedades dos documentos participantes se e somente se ambos os documentos atenderem às condições especificadas no ON
cláusula.
Por exemplo, considerando o modelo de dados que apresentamos anteriormente, vamos supor que você queira buscar o firstName
, lastName
de um "funcionário" e o correspondente nome
do "departamento" ao qual o funcionário pertencia. Nesse caso, primeiro nome
e sobrenome
as propriedades são obtidas do documento de tipo
"funcionário" e o departamento nome
é obtido do documento de tipo
"departamento" se e somente se o departamento
da propriedade "employee" corresponde à propriedade código
propriedade no "departamento"
Isso implica que, se não houver documentos de "departamento" que correspondam ao código
no documento "employee", os detalhes desse funcionário não serão incluídos no resultado de saída

Solicitação
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
tentar db.createIndex(IndexBuilder.valueIndex(itens: ValueIndexItem.propriedade("tipo")),withName: "typeIndex") // configurar aliases para representar a fonte de dados para o documento do tipo "department deixar departamentoDS = Fonte de dados.banco de dados(db).como("departmentDS") // Expressão de propriedade para "department" (em documentos de funcionários) deixar employeeDeptExpr = Expressão.propriedade("departamento").de("employeeDS") // Expressão de propriedade para "code" (em documentos de departamento) deixar departmentCodeExpr = Expressão.propriedade("código").de("departmentDS") // Cláusula de união: Unir documentos do tipo funcionário e do tipo departamento onde o // O campo "department" dos documentos do funcionário é igual ao "código" do departamento; // de documentos de "departamento deixar joinExpr = employeeDeptExpr.igual a(departmentCodeExpr) .e(Expressão.propriedade("tipo").de("employeeDS").igual a(Expressão.string("funcionário"))) .e(Expressão.propriedade("tipo").de("departmentDS").igual a(Expressão.string("departamento"))) // Construa a expressão de junção interna com a consulta ON. deixar unir-se = Aderir.unir-se(departamentoDS).em(joinExpr) // retorne os nomes "firstname", "lastname" e "department" dos documentos unidos com base em // na expressão JOIN deixar pesquisa = Consulta.selecionar( SelecionarResultado.expressão(Expressão.propriedade("firstname").de("employeeDS")), SelecionarResultado.expressão(Expressão.propriedade("lastname" (sobrenome)).de("employeeDS")), SelecionarResultado.expressão(Expressão.propriedade("name" (nome)).de("departmentDS"))) .de(funcionárioDS) .unir-se(unir-se) |
ANSI SQL
A instrução SQL equivalente para a consulta acima seria
1 2 3 4 5 6 7 8 9 10 |
SELECIONAR employeeDS.firstname, employeeDS.lastname, departmentDS.name DE `viagem-amostra funcionárioDS INNER JOIN `viagem-amostra departamentoDS ON employeeDS.department = departmentDS.code ONDE empregadoDS.tipo = "funcionário" E departamentoDS.tipo = "departamento" |
Left JOIN ou Left Outer JOIN
Você pode usar uma consulta JOIN à esquerda para obter propriedades dos documentos participantes se ambos os documentos atenderem às condições especificadas no ON
cláusula. No entanto, diferentemente de um JOIN regular, os resultados também incluirão documentos não correspondentes no lado esquerdo da cláusula ON
da expressão JOIN.
Por exemplo, considerando o modelo de dados que apresentamos anteriormente, vamos supor que você queira buscar o firstName
, lastName
de um "funcionário" e o correspondente nome
do "departamento" ao qual o funcionário pertencia.
Além disso, vamos supor que também estejamos interessados nofirstName
e lastName
de um "funcionário" cujo departamento
código faz não corresponde a um departamento válido. Esse poderia ser o caso, por exemplo, se o departamento
O código do funcionário foi inserido incorretamente.
Nesse caso, primeiro nome
e sobrenome
as propriedades são obtidas do documento de tipo
"funcionário" e o departamento nome
é obtido do documento de tipo
"departamento" se o departamento
da propriedade "employee" corresponde à propriedade código
propriedade no "departamento".
Se não houver um departamento correspondente, somente o primeiro nome
e sobrenome
do documento "employee" são retornadas.

Solicitação
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// configurar aliases para representar a fonte de dados para o documento do tipo "employee". deixar funcionárioDS = Fonte de dados.banco de dados(db).como("employeeDS") // configurar aliases para representar a fonte de dados para o documento do tipo "department deixar departamentoDS = Fonte de dados.banco de dados(db).como("departmentDS") // Expressão de propriedade para "department" (em documentos de funcionários) deixar employeeDeptExpr = Expressão.propriedade("departamento").de("employeeDS") // Expressão de propriedade para "code" (em documentos de departamento) deixar departmentCodeExpr = Expressão.propriedade(&cotação;código&cotação;).de("departmentDS") // Cláusula de união: Unir documentos de tipo de departamento e tipo de funcionário onde o // O campo "department" dos documentos do funcionário é igual ao "código" do departamento de // documentos do "departamento deixar joinExpr = employeeDeptExpr.igual a(departmentCodeExpr) .e(Expressão.propriedade("tipo").de("employeeDS").igual a(Expressão.string("funcionário"))) .e(Expressão.propriedade("tipo").de("departmentDS").igual a(Expressão.string("departamento"))) // expressão de união com consulta ON deixar unir-se = Aderir.leftJoin(departamentoDS).em(joinExpr) // retorna os nomes "firstname", "lastname" e "department" dos documentos que foram unidos com base na expressão JOIN deixar pesquisa = Consulta.selecionar( SelecionarResultado.expressão(Expressão.propriedade("firstname").de("employeeDS")), SelecionarResultado.expressão(Expressão.propriedade("lastname" (sobrenome)).de("employeeDS")), SelecionarResultado.expressão(Expressão.propriedade("name" (nome)).de("departmentDS"))) .de(funcionárioDS) .unir-se(unir-se) |
ANSI SQL
A instrução SQL equivalente para a consulta acima seria
1 2 3 4 5 6 7 8 9 10 |
SELECIONAR employeeDS.firstname, employeeDS.lastname, departmentDS.name DE `viagem-amostra funcionárioDS JUNÇÃO ESQUERDA `viagem-amostra departamentoDS ON employeeDS.department = departmentDS.code ONDE empregadoDS.tipo = "funcionário" E departamentoDS.tipo = "departamento" |
Cross JOIN
Você pode usar uma consulta cross JOIN para buscar o produto cartesiano das propriedades dos documentos participantes, que normalmente não estão relacionados entre si. Isso é o equivalente a um inner JOIN sem o ON
da expressão de união.
Por exemplo, considerando o modelo de dados que apresentamos anteriormente, vamos supor que você queira buscar o produto cartesiano de todos os documentos de tipo
"localização" e tipo
"departamento". Em outras palavras, cada "local" tipo
seria combinado com cada um dos documentos do "departamento" tipo
documentos.
Como não há em
especificada na expressão cross JOIN, você precisaria incluir uma cláusula onde
para filtrar o subconjunto de documentos a ser considerado com base no documento tipo
.

Solicitação
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// configurar aliases para representar a fonte de dados para o documento do tipo "department deixar departamentoDS = Fonte de dados.banco de dados(db).como("departmentDS") // configurar aliases para representar a fonte de dados para o documento do tipo "location" deixar localizaçãoDS = Fonte de dados.banco de dados(db).como("locationDS") // Expressão de propriedade para "department" (em documentos de funcionários) deixar employeeDeptExpr = Expressão.propriedade("código").de("departmentDS") // Expressão de propriedade para "department" (em documentos de localização) deixar departmentCodeExpr = Expressão.propriedade("código").de("locationDS") // expressão de junção cruzada deixar unir-se = Aderir.crossJoin(localizaçãoDS) // expressões de propriedade de tipo deixar deptTypeExpr = Expressão.propriedade("tipo").de("departmentDS") deixar locationTypeExpr = Expressão.propriedade("tipo").de("locationDS") // As cláusulas where filtram o conjunto de documentos para fazer a junção cruzada // Atribuímos um alias às propriedades "code", pois elas existem nos documentos de departamento e de local // OBSERVAÇÃO: a cláusula where é usada para filtrar os documentos a serem considerados como // parte da união cartesiana deixar pesquisa = Consulta.selecionar( SelecionarResultado.expressão(Expressão.propriedade("name" (nome)).de("departmentDS")).como("DeptName"), SelecionarResultado.expressão(Expressão.propriedade("name" (nome)).de("locationDS")).como("LocationName" (nome do local)), SelecionarResultado.expressão(Expressão.propriedade("endereço").de("locationDS"))) .de(departamentoDS) .unir-se(unir-se) .onde(deptTypeExpr.igual a(Expressão.string("departamento")) .e(locationTypeExpr.igual a(Expressão.string("localização")))) |
ANSI SQL
A instrução SQL equivalente para a consulta acima seria
1 2 3 4 5 6 7 8 9 |
SELECIONAR departmentDS.name AS DeptName, locationDS.name AS LocationName, localizaçãoDS.endereço DE `viagem-amostra departamentoDS JUNÇÃO CRUZADA `viagem-amostra localizaçãoDS ONDE departamentoDS.tipo = "departamento" |
Encadeamento de JOINs
É possível especificar várias expressões JOIN em seu selecionar
para poder unir-se a documentos com base em critérios diferentes.
Por exemplo, considerando o modelo de dados que apresentamos anteriormente, vamos supor que você queira buscar o firstName
, lastName
de um "funcionário" e o correspondente nome
do "departamento" ao qual o funcionário pertencia. Além disso, você também queria identificar o nome
do "local" em que o funcionário estava baseado.
Nesse caso, usamos duas expressões JOIN.
A primeira expressão JOIN é usada para unir documentos de tipo
"funcionário" com documentos de tipo
"department" com base na propriedade "department code". Nesse caso, a propriedade primeiro nome
e sobrenome
as propriedades são obtidas do documento de tipo
"funcionário" e o departamento nome
é obtido do documento de tipo
"departamento" se e somente se o departamento
da propriedade "employee" corresponde à propriedade código
propriedade no "departamento".
A segunda expressão JOIN é usada para unir documentos de tipo
"funcionário" com documentos de tipo
"location" com base na propriedade "location code". Nesse caso, a propriedade primeiro nome
e sobrenome
as propriedades são obtidas do documento de tipo
"funcionário" e o local nome
é obtido do documento de tipo
"localização" se e somente se o localização
da propriedade "employee" corresponde à propriedade código
propriedade no "departamento".

Solicitação
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// configurar aliases para representar a fonte de dados para o documento do tipo "employee". deixar funcionárioDS = Fonte de dados.banco de dados(db).como("employeeDS") // configurar aliases para representar a fonte de dados para o documento do tipo "department deixar departamentoDS = Fonte de dados.banco de dados(db).como("departmentDS") // configurar aliases para representar a fonte de dados para o documento do tipo "location" deixar localizaçãoDS = Fonte de dados.banco de dados(db).como("locationDS") // Expressão de propriedade para a propriedade "department" (em documentos de funcionários) deixar employeeDeptExpr = Expressão.propriedade("departamento").de("employeeDS") // Expressão de propriedade para a propriedade "location" (em documentos de funcionários) deixar employeeLocationExpr = Expressão.propriedade("localização").de("employeeDS") // Expressão de propriedade para a propriedade "code" (em documentos de departamento) deixar departmentCodeExpr = Expressão.propriedade("código").de("departmentDS") // Expressão de propriedade para a propriedade "code" (em documentos de localização) deixar locationCodeExpr = Expressão.propriedade("código").de("locationDS") // Critérios de união 1 // União em que o campo "department" dos documentos do funcionário é igual ao "department code" dos documentos do "department" deixar joinDeptCodeExpr = employeeDeptExpr.igual a(departmentCodeExpr) .e(Expressão.propriedade("tipo").de("employeeDS").igual a(Expressão.string("funcionário"))) .e(Expressão.propriedade("tipo").de("departmentDS").igual a(Expressão.string("departamento"))) // Critérios de união 2 // União em que o campo "department" dos documentos do funcionário é igual ao "department code" dos documentos do "department" deixar joinLocationCodeExpr = employeeLocationExpr.igual a(locationCodeExpr) .e(Expressão.propriedade("tipo").de("employeeDS").igual a(Expressão.string("funcionário"))) .e(Expressão.propriedade("tipo").de("locationDS").igual a(Expressão.string("localização"))) // expressão de união para o código do departamento deixar joinDeptCode = Aderir.unir-se(departamentoDS).em(joinDeptCodeExpr) // expressão de união para o código de localização deixar joinLocationCode = Aderir.unir-se(localizaçãoDS).em(joinLocationCodeExpr) // Várias expressões de união na cláusula de união deixar pesquisa = Criador de consultas.selecionar(SelecionarResultado.expressão(Expressão.propriedade("firstname").de("employeeDS")), SelecionarResultado.expressão(Expressão.propriedade("lastname" (sobrenome)).de("employeeDS")), SelecionarResultado.expressão(Expressão.propriedade("name" (nome)).de("departmentDS")).como("deptName"), SelecionarResultado.expressão(Expressão.propriedade("name" (nome)).de("locationDS")).como("locationName")) .de(funcionárioDS) .unir-se(joinDeptCode,joinLocationCode) |
ANSI SQL
A instrução SQL equivalente para a consulta acima seria
1 2 3 4 5 6 7 8 9 10 11 12 13 |
SELECIONAR employeeDS.firstname, employeeDS.lastname, departmentDS.name AS deptName, locationDS.name AS locationName DE `viagem-amostra funcionárioDS JUNTAR `viagem-amostra departamentoDS ON employeeDS.department = departmentDS.code JUNTAR `viagem-amostra localizaçãoDS ON employeeDS.location = localizaçãoDS.código ONDE departamentoDS.tipo = "departamento" E localizaçãoDS.tipo = "localização" E empregadoDS.tipo = "funcionário" |
JOIN Expressões com funções
Embora todos os exemplos tenham usado o igual a
na expressão JOIN, deve-se observar que você pode usar qualquer operador de comparação, como entre
, greaterThanOrEqualTo
e assim por diante na expressão JOIN. Você também pode incluir qualquer Função
expressões. Esse é um recurso poderoso.
Por exemplo, considerando o modelo de dados que apresentamos anteriormente, vamos supor que você queira buscar o departamento nome
e correspondente localização
nomes do "local" onde o departamento estava baseado. Um departamento pode pertencer a um ou mais locais.
Nesse caso, a expressão JOIN uniria documentos do tipo "department" (departamento) e "location" (local) procurando correspondências em qualquer um dos membros da tabela localização
do documento do departamento usando a propriedade de matriz ArrayFunction
expressão.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// configurar aliases para representar a fonte de dados para o documento do tipo "department deixar departamentoDS = Fonte de dados.banco de dados(db).como("departmentDS") // configurar aliases para representar a fonte de dados para o documento do tipo "location" deixar localizaçãoDS = Fonte de dados.banco de dados(db).como("locationDS") // Expressão de propriedade para a propriedade "location" (em documentos de departamento) deixar departmentLocationExpr = Expressão.propriedade("localização").de("departmentDS") // Expressão de propriedade para a propriedade "code" (em documentos de localização) deixar locationCodeExpr = Expressão.propriedade("código").de("locationDS") // Ingressar onde o campo "code" dos documentos de localização está contido // a matriz "location" dos documentos "department" (departamento) deixar joinDeptCodeExpr = ArrayFunction.contém(departmentLocationExpr, valor: locationCodeExpr) .e(Expressão.propriedade("tipo").de("locationDS").igual a(Expressão.string("localização")) .e(Expressão.propriedade("tipo").de("departmentDS").igual a(Expressão.string("departamento")))) // expressão de união deixar joinLocationCode = Aderir.unir-se(departamentoDS).em(joinDeptCodeExpr) // Consulta de pesquisa com JOIN deixar pesquisa = Criador de consultas.selecionar( SelecionarResultado.expressão(Expressão.propriedade("name" (nome)).de("departmentDS")).como("departmentName" (nome do departamento)), SelecionarResultado.expressão(Expressão.propriedade("name" (nome)).de("locationDS")).como("locationName")) .de(localizaçãoDS) .unir-se(joinLocationCode) |
ANSI SQL
As matrizes não são compatíveis com o SQL. Entretanto, o N1QL inclui suporte para matrizes. A instrução do tipo SQL correspondente para a consulta acima seria
1 2 3 4 5 6 7 8 9 10 11 12 |
SELECIONAR departmentDS.name AS departmentName, locationDS.name AS locationName DE `viagem-amostra localizaçãoDS JUNTAR `viagem-amostra departamentoDS ON QUALQUER código IN departmentDS.location SATISFAÇÕES código = localizaçãoDS.location FIM ONDE departamentoDS.tipo = "departamento" E localizaçãoDS.tipo = "localização" |
O que vem a seguir
Esta postagem do blog analisou o poderoso recurso JOIN do Couchbase Mobile 2.0, que permite combinar resultados de vários documentos JSON. Você pode download Couchbase Mobile 2.0 e teste as consultas discutidas nesta publicação. Isso é um começo. Espere ver mais funcionalidades em versões futuras.
Aqui estão algumas outras postagens relacionadas ao Couchbase Mobile Query que podem ser de interesse
- Isso postagem no blog discute os fundamentos
- Isso postagem no blog discute como consultar coleções de matrizes
- Isso postagem no blog discute os recursos de pesquisa de texto completo (FTS).
Se tiver dúvidas ou comentários, deixe um comentário abaixo ou entre em contato comigo pelo Twitter @rajagp ou envie-me um e-mail priya.rajagopal@couchbase.com. O Fóruns do Couchbase são outro bom lugar para entrar em contato com perguntas.