Couchbase Lite 2.0 soporta la capacidad de realizar JOINS a través de tus documentos JSON . Esto es parte de la nueva interfaz de consulta basada en N1QLel lenguaje de consulta declarativo de Couchbase que extiende SQL para JSON. Si estás familiarizado con SQL, te sentirás como en casa con la semántica de la nueva API.
Los JOINS permiten combinar el contenido de múltiples documentos. En este post, proporcionaremos ejemplos para ilustrar los tipos de JOINS posibles con Couchbase Lite. Para cada una de las consultas, proporcionaremos la consulta SQL equivalente. Este blog asume que estás familiarizado con los fundamentos de la nueva API de consultas, así que si aún no lo has hecho, asegúrate de revisar la sección entrada anterior primero. Si le interesa, al final de esta entrada encontrará enlaces a blogs en los que se tratan otras características de la interfaz de consulta.
Puede descargar las últimas versiones de Couchbase Mobile 2.0 Pre-Release desde nuestra página web descargas página.
Fondo
Si estabas usando versiones 1.x de Couchbase Mobile, probablemente estés familiarizado con Map-Views para crear índices y consultas. En la versión 2.0, ya no es necesario crear vistas y funciones de mapa. En su lugar, una sencilla interfaz le permite crear índices y puede utilizar una interfaz Query Builder para construir sus consultas. En comparación, la nueva interfaz de consulta es más sencilla de utilizar y mucho más potente. Descubriremos algunas de sus características en este post.
Ejemplo de proyecto
Aunque en los ejemplos aquí expuestos se utiliza Swift para iOS, la misma interfaz de consulta es compatible también con las plataformas Android y Windows.
Así que, con unos pequeños ajustes, deberías poder reutilizar los ejemplos de consulta de este artículo cuando trabajes con otras plataformas.
Siga las instrucciones siguientes si está interesado en un proyecto Swift de muestra
- Clonar el iOS Swift Playground desde GitHub
|
1 |
$ git clonar https://github.com/couchbaselabs/couchbase-lite-ios-api-playground |
- Siga las instrucciones de instalación en el LÉAME para construir y ejecutar la zona de juegos.
Modelo de datos de muestra
Utilizaremos una base de datos de muestra situada aquí. Puede incrustar esta base de datos preconstruida en su aplicación móvil y empezar a utilizarla para sus consultas.
El conjunto de datos de muestra es un poco artificioso, pero el objetivo es demostrar algunos casos típicos de uso de únase a preguntas.
- "documento de tipo "empleado
|
1 2 3 4 5 6 7 |
{ "tipo": "empleado", "nombre": "John", "apellido": "Smith", "departamento": "1000", "localización": "101" } |
- "documento de tipo "departamento
|
1 2 3 4 5 6 7 8 9 10 11 |
{ "tipo": "departamento", "nombre": "Gestión de productos", "código": "2000", "cabeza": { "nombre": "Patricia", "apellido": "Shoji" }, "localización":["101","102"] } |
- "documento de tipo "localización
|
1 2 3 4 5 6 |
{ "tipo": "localización", "nombre": "CUARTEL GENERAL", "dirección": " 1123 6th St. Melbourne, FL 32904 ", "código": "101" } |
** Consulte el modelo anterior para cada uno de los ejemplos de consulta a continuación. **
El asa de la base de datos
En las consultas siguientes, utilizaremos la función Base de datos API para abrir/crear Base de Datos CouchbaseLite.
|
1 2 |
deje opciones = Configuración de la base de datos() deje db = pruebe Base de datos(nombre: kDBName, config: opciones) |
Índices
Para acelerar las consultas de lectura, puede crear índices sobre las propiedades que va a consultar. La mejora del rendimiento sería significativa en grandes conjuntos de datos. Por supuesto, tenga en cuenta que habrá un aumento en las necesidades de almacenamiento para almacenar los índices y el rendimiento de las escrituras también puede verse afectado. Por lo tanto, tenga cuidado al crear demasiados índices.
El siguiente ejemplo crea un ÍndiceValor en el tipo de un documento
|
1 |
pruebe db.createIndex(IndexBuilder.valueIndex(artículos: ValueIndexItem.propiedad("tipo")),withName: "typeIndex") |
JOIN o Inner JOIN
Puede utilizar una consulta JOIN simple o Inner JOIN para obtener propiedades de los documentos participantes si y sólo si ambos documentos cumplen las condiciones especificadas en el campo EN cláusula.
Por ejemplo, teniendo en cuenta el modelo de datos que hemos presentado anteriormente, supongamos que desea obtener la información firstName, apellido de un "empleado" y la correspondiente nombre del "departamento" al que pertenecía el empleado. En este caso, nombre y apellido se obtienen del documento de tipo "empleado" y el departamento nombre se obtiene del documento de tipo "departamento" si y sólo si el departamento del "empleado" coincide con la propiedad código propiedad en el "departamento
Esto implica que si no hay documentos del "departamento" que coincidan con el código en el documento "empleado", los datos de ese empleado no se incluyen en el resultado de salida

Solicitar
|
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 |
pruebe db.createIndex(IndexBuilder.valueIndex(artículos: ValueIndexItem.propiedad("tipo")),withName: "typeIndex") // configurar alias para representar la fuente de datos del documento de tipo "departamento deje departamentoDS = Fuente de datos.base de datos(db).como("departamentoDS") // Expresión de propiedad para "departamento" (en documentos de empleado) deje empleadoDeptExpr = Expresión.propiedad("departamento").de("empleadoDS") // Expresión de propiedad para "código" (en documentos de departamento) deje departamentoCodeExpr = Expresión.propiedad("código").de("departamentoDS") // Cláusula de unión: Unir documentos de tipo empleado y de tipo departamento donde el // El campo "departamento" de los documentos del empleado es igual al "código" del departamento; // de documentos "departamento deje joinExpr = empleadoDeptExpr.equalTo(departamentoCodeExpr) .y(Expresión.propiedad("tipo").de("empleadoDS").equalTo(Expresión.cadena("empleado"))) .y(Expresión.propiedad("tipo").de("departamentoDS").equalTo(Expresión.cadena("departamento"))) // Construir expresión inner join con consulta ON. deje únase a = Únete a.únase a(departamentoDS).en(joinExpr) // devolver el "nombre", "apellido"; y "departamento"; nombre de los documentos que se unen en base a // en la expresión JOIN deje searchQuery = Consulta.seleccione( SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("empleadoDS")), SeleccionarResultado.expresión(Expresión.propiedad("apellido").de("empleadoDS")), SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("departamentoDS"))) .de(employeeDS) .únase a(únase a) |
ANSI SQL
La sentencia SQL equivalente para la consulta anterior sería
|
1 2 3 4 5 6 7 8 9 10 |
SELECCIONE empleadoDS.nombre, empleadoDS.apellido, departamentoDS.name DESDE Viajar-muestra employeeDS INNER JOIN Viajar-muestra departamentoDS EN empleadoDS.departamento = departamentoDS.code DONDE empleadosDS.tipo = "empleado" Y departamentos.tipo = "departamento" |
Left JOIN o Left Outer JOIN
Puede utilizar una consulta JOIN izquierda para obtener propiedades de los documentos participantes si ambos documentos cumplen las condiciones especificadas en el campo EN . Sin embargo, a diferencia de un JOIN normal, los resultados también incluirán los documentos no coincidentes a la izquierda de la cláusula EN de la expresión JOIN.
Por ejemplo, teniendo en cuenta el modelo de datos que hemos presentado anteriormente, supongamos que desea obtener la información firstName, apellido de un "empleado" y la correspondiente nombre del "departamento" al que pertenecía el empleado.
Además, supongamos que también nos interesa elfirstName y apellido de un "empleado" cuyo departamento código hace no corresponden a un departamento válido. Este podría ser el caso, por ejemplo, si el departamento el código del empleado se ha introducido incorrectamente.
En este caso, nombre y apellido se obtienen del documento de tipo "empleado" y el departamento nombre se obtiene del documento de tipo "departamento" si el departamento del "empleado" coincide con la propiedad código en el "departamento".
Si no hay ningún departamento que coincida, entonces sólo el nombre y apellido del documento "empleado".

Solicitar
|
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 |
// establecer alias para representar la fuente de datos del documento de tipo "empleado deje employeeDS = Fuente de datos.base de datos(db).como("empleadoDS") // configurar alias para representar la fuente de datos del documento de tipo "departamento deje departamentoDS = Fuente de datos.base de datos(db).como("departamentoDS") // Expresión de propiedad para "departamento" (en documentos de empleado) deje empleadoDeptExpr = Expresión.propiedad("departamento").de("empleadoDS") // Expresión de propiedad para "código" (en documentos de departamento) deje departamentoCodeExpr = Expresión.propiedad("código").de("departamentoDS") // Cláusula de unión: Unir documentos de tipo departamento y de tipo empleado donde el // el campo "departamento" de los documentos de los empleados es igual al "código" de departamento de // documentos "departamento deje joinExpr = empleadoDeptExpr.equalTo(departamentoCodeExpr) .y(Expresión.propiedad("tipo").de("empleadoDS").equalTo(Expresión.cadena("empleado"))) .y(Expresión.propiedad("tipo").de("departamentoDS").equalTo(Expresión.cadena("departamento"))) // expresión de unión con consulta ON deje únase a = Únete a.leftJoin(departamentoDS).en(joinExpr) // devuelve el nombre "firstname", "lastname" y "department" de los documentos que se unen en base a la expresión JOIN deje searchQuery = Consulta.seleccione( SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("empleadoDS")), SeleccionarResultado.expresión(Expresión.propiedad("apellido").de("empleadoDS")), SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("departamentoDS"))) .de(employeeDS) .únase a(únase a) |
ANSI SQL
La sentencia SQL equivalente para la consulta anterior sería
|
1 2 3 4 5 6 7 8 9 10 |
SELECCIONE empleadoDS.nombre, empleadoDS.apellido, departamentoDS.name DESDE Viajar-muestra employeeDS LEFT JOIN Viajar-muestra departamentoDS EN empleadoDS.departamento = departamentoDS.code DONDE empleadosDS.tipo = "empleado" Y departamentos.tipo = "departamento" |
Cruz JOIN
Puede utilizar una consulta JOIN cruzada para obtener el producto cartesiano de las propiedades de los documentos participantes, que normalmente no están relacionados entre sí. Es el equivalente de un JOIN interno sin el EN de la expresión join.
Por ejemplo, teniendo en cuenta el modelo de datos que hemos presentado anteriormente, supongamos que desea obtener el producto cartesiano de todos los documentos de tipo "ubicación" y tipo "departamento". En otras palabras, cada "ubicación" tipo documento se combinaría con cada uno de los "departamentos" tipo documentos.
Dado que no hay en especificada en la expresión cross JOIN, deberá incluir una cláusula donde para filtrar el subconjunto de documentos a considerar en función del documento tipo.

Solicitar
|
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 alias para representar la fuente de datos del documento de tipo "departamento deje departamentoDS = Fuente de datos.base de datos(db).como("departamentoDS") // establecer alias para representar la fuente de datos del documento de tipo "localización deje ubicaciónDS = Fuente de datos.base de datos(db).como("locationDS") // Expresión de propiedad para "departamento" (en documentos de empleado) deje empleadoDeptExpr = Expresión.propiedad("código").de("departamentoDS") // Expresión de propiedad para "departamento" (en documentos de localización) deje departamentoCodeExpr = Expresión.propiedad("código").de("locationDS") // expresión de unión cruzada deje únase a = Únete a.crossJoin(ubicaciónDS) // expresiones de propiedad de tipo deje deptTypeExpr = Expresión.propiedad("tipo").de("departamentoDS") deje locationTypeExpr = Expresión.propiedad("tipo").de("locationDS") // Las cláusulas where filtran el conjunto de documentos sobre los que se va a realizar la unión cruzada // Ponemos un alias a las propiedades "code" ya que existe tanto en los documentos de departamento como en los de localización // NOTA: La cláusula where se utiliza para filtrar los documentos a considerar como // parte de la unión cartesiana deje searchQuery = Consulta.seleccione( SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("departamentoDS")).como("DeptName"), SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("locationDS")).como("LocationName"), SeleccionarResultado.expresión(Expresión.propiedad("dirección").de("locationDS"))) .de(departamentoDS) .únase a(únase a) .donde(deptTypeExpr.equalTo(Expresión.cadena("departamento")) .y(locationTypeExpr.equalTo(Expresión.cadena("localización")))) |
ANSI SQL
La sentencia SQL equivalente para la consulta anterior sería
|
1 2 3 4 5 6 7 8 9 |
SELECCIONE departamentoDS.name AS DeptName, locationDS.name AS LocationName, ubicaciónDS.dirección DESDE Viajar-muestra departamentoDS CROSS JOIN Viajar-muestra ubicaciónDS DONDE departamentos.tipo = "departamento" |
Encadenamiento de JOINs
Es posible especificar varias expresiones JOIN en el campo seleccione para poder realizar JOIN entre documentos basados en diferentes criterios.
Por ejemplo, teniendo en cuenta el modelo de datos que hemos presentado anteriormente, supongamos que desea obtener la información firstName, apellido de un "empleado" y la correspondiente nombre del "departamento" al que pertenecía el empleado. Además, también quería identificar el nombre del "lugar" en el que estaba destinado el empleado.
En este caso, utilizamos dos expresiones JOIN.
La primera expresión JOIN se utiliza para JOIN documentos de tipo "empleado" con documentos de tipo "departamento" en función de la propiedad "código de departamento". En este caso, la propiedad nombre y apellido se obtienen del documento de tipo "empleado" y el departamento nombre se obtiene del documento de tipo "departamento" si y sólo si el departamento del "empleado" coincide con la propiedad código en el "departamento".
La segunda expresión JOIN se utiliza para JOIN documentos de tipo "empleado" con documentos de tipo "ubicación" en función de la propiedad "código de ubicación". En este caso, la propiedad nombre y apellido se obtienen del documento de tipo "empleado" y la ubicación nombre se obtiene del documento de tipo "ubicación" si y sólo si el ubicación del "empleado" coincide con la propiedad código en el "departamento".

Solicitar
|
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 |
// establecer alias para representar la fuente de datos del documento de tipo "empleado deje employeeDS = Fuente de datos.base de datos(db).como("empleadoDS") // configurar alias para representar la fuente de datos del documento de tipo "departamento deje departamentoDS = Fuente de datos.base de datos(db).como("departamentoDS") // establecer alias para representar la fuente de datos del documento de tipo "localización deje ubicaciónDS = Fuente de datos.base de datos(db).como("locationDS") // Expresión de propiedad para la propiedad "departamento" (en documentos de empleado) deje empleadoDeptExpr = Expresión.propiedad("departamento").de("empleadoDS") // Expresión de propiedad para la propiedad "ubicación" (en documentos de empleado) deje employeeLocationExpr = Expresión.propiedad("localización").de("empleadoDS") // Expresión de propiedad para la propiedad "code" (en documentos de departamento) deje departamentoCodeExpr = Expresión.propiedad("código").de("departamentoDS") // Expresión de propiedad para la propiedad "code" (en documentos de localización) deje locationCodeExpr = Expresión.propiedad("código").de("locationDS") // Criterio de unión 1 // Unir donde el campo "departamento" de los documentos del empleado es igual al "código" de departamento de los documentos del "departamento deje joinDeptCodeExpr = empleadoDeptExpr.equalTo(departamentoCodeExpr) .y(Expresión.propiedad("tipo").de("empleadoDS").equalTo(Expresión.cadena("empleado"))) .y(Expresión.propiedad("tipo").de("departamentoDS").equalTo(Expresión.cadena("departamento"))) // Criterios de unión 2 // Unir donde el campo "departamento" de los documentos del empleado es igual al "código" de departamento de los documentos del "departamento deje joinLocationCodeExpr = employeeLocationExpr.equalTo(locationCodeExpr) .y(Expresión.propiedad("tipo").de("empleadoDS").equalTo(Expresión.cadena("empleado"))) .y(Expresión.propiedad("tipo").de("locationDS").equalTo(Expresión.cadena("localización"))) // expresión de unión para el código de departamento deje joinDeptCode = Únete a.únase a(departamentoDS).en(joinDeptCodeExpr) // expresión de unión para el código de ubicación deje joinCódigoDeLocalización = Únete a.únase a(ubicaciónDS).en(joinLocationCodeExpr) // Múltiples expresiones join en la cláusula join deje searchQuery = Constructor de consultas.seleccione(SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("empleadoDS")), SeleccionarResultado.expresión(Expresión.propiedad("apellido").de("empleadoDS")), SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("departamentoDS")).como("deptName"), SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("locationDS")).como("locationName")) .de(employeeDS) .únase a(joinDeptCode,joinCódigoDeLocalización) |
ANSI SQL
La sentencia SQL equivalente para la consulta anterior sería
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
SELECCIONE empleadoDS.nombre, empleadoDS.apellido, departamentoDS.name AS deptName, locationDS.name AS locationName DESDE Viajar-muestra employeeDS ÚNASE A Viajar-muestra departamentoDS EN empleadoDS.departamento = departamentoDS.code ÚNASE A Viajar-muestra ubicaciónDS EN employeeDS.location = ubicaciónDS.code DONDE departamentos.tipo = "departamento" Y localizaciónDS.tipo = "localización" Y empleadosDS.tipo = "empleado" |
Unir expresiones con funciones
Aunque en todos los ejemplos se utilizó el equalTo en la expresión JOIN, debe tenerse en cuenta que se puede utilizar cualquier operador de comparación, como por ejemplo entre, greaterThanOrEqualTo etc. en la expresión JOIN. También puede incluir cualquier Función expresiones. Se trata de una función muy potente.
Por ejemplo, teniendo en cuenta el modelo de datos que hemos presentado anteriormente, supongamos que desea obtener el departamento nombre y la correspondiente ubicación nombres de las "sedes" en las que tenía su sede el departamento. Un departamento puede pertenecer a una o varias sedes.
En este caso, la expresión JOIN uniría documentos de tipo "departamento" y "ubicación" buscando coincidencias en cualquiera de los miembros de la cadena ubicación del documento del departamento utilizando la propiedad ArrayFunction expresión.

|
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 alias para representar la fuente de datos del documento de tipo "departamento deje departamentoDS = Fuente de datos.base de datos(db).como("departamentoDS") // establecer alias para representar la fuente de datos del documento de tipo "localización deje ubicaciónDS = Fuente de datos.base de datos(db).como("locationDS") // Expresión de propiedad para la propiedad "ubicación" (en documentos de departamento) deje departamentoLocalizaciónExpr = Expresión.propiedad("localización").de("departamentoDS") // Expresión de propiedad para la propiedad "code" (en documentos de localización) deje locationCodeExpr = Expresión.propiedad("código").de("locationDS") // Únase al campo "código" de los documentos de localización // la matriz "ubicación" de los documentos "departamento deje joinDeptCodeExpr = ArrayFunction.contiene(departamentoLocalizaciónExpr, valor: locationCodeExpr) .y(Expresión.propiedad("tipo").de("locationDS").equalTo(Expresión.cadena("localización")) .y(Expresión.propiedad("tipo").de("departamentoDS").equalTo(Expresión.cadena("departamento")))) // expresión de unión deje joinCódigoDeLocalización = Únete a.únase a(departamentoDS).en(joinDeptCodeExpr) // Consulta de búsqueda con JOIN deje searchQuery = Constructor de consultas.seleccione( SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("departamentoDS")).como("departmentName"), SeleccionarResultado.expresión(Expresión.propiedad("nombre").de("locationDS")).como("locationName")) .de(ubicaciónDS) .únase a(joinCódigoDeLocalización) |
ANSI SQL
Las matrices no están soportadas en SQL. Sin embargo, N1QL incluye soporte para arrays. La sentencia SQL correspondiente para la consulta anterior sería
|
1 2 3 4 5 6 7 8 9 10 11 12 |
SELECCIONE departamentoDS.name AS departmentName, locationDS.name AS locationName DESDE Viajar-muestra ubicaciónDS ÚNASE A Viajar-muestra departamentoDS EN CUALQUIER código EN departamentoDS.ubicación SATISFACE código = locationDS.location FIN DONDE departamentos.tipo = "departamento" Y localizaciónDS.tipo = "localización" |
¿Qué sigue?
Esta entrada de blog revisó la poderosa característica JOIN en Couchbase Mobile 2.0 que te permite combinar resultados de múltiples documentos JSON. Puedes descargar Couchbase Mobile 2.0 y probar las consultas discutidas en este post. Esto es un comienzo. Espera ver más funcionalidades en futuras versiones.
Aquí hay algunos otros posts relacionados con Couchbase Mobile Query que pueden ser de interés
- Este entrada del blog analiza los fundamentos
- Este entrada del blog explica cómo consultar colecciones de matrices
- Este entrada del blog discute las capacidades de Búsqueda de Texto Completo (FTS).
Si tiene alguna pregunta o sugerencia, deje un comentario a continuación o póngase en contacto conmigo en Twitter @rajagp o envíeme un correo electrónico priya.rajagopal@couchbase.com. En Foros de Couchbase son otro buen lugar para plantear preguntas.