Recientemente he añadido soporte a nuestro cliente Node.js para ejecutar consultas N1QL contra tu cluster, siempre que estés ejecutando una instancia del motor N1QL (para conseguir la versión actualizada del cliente Node.js con este soporte, apunta npm a nuestra rama maestra de github en https://github.com/couchbase/couchnode). Cuando lo implementé, no tenía mucho para probar en ese momento, así que pensé que sería un esfuerzo interesante ver lo bien que se vería el ejemplo de la muestra de cerveza de Node.js si usáramos consultas N1QL en lugar de usar vistas.

Primero empecé convirtiendo las consultas básicas que simplemente seleccionaban todas las cervezas o cervecerías de los datos de muestra, y luego pasé a convertir las consultas de búsqueda en vivo para utilizar también N1QL. Pensé en escribir una pequeña entrada en el blog sobre las conversiones y hacer algunas observaciones sobre lo que noté en el camino.

He aquí nuestra primera consulta:

var q = {
  límite : ENTRADAS_POR_PÁGINA,
  rancio : falso
};
db.ver( "cerveza", "by_name", q).consulta(función(err, valores) {
  var llaves = _.desplumar(valores, id);
  db.getMulti( llaves, null, función(err, resultados) {
    var cervezas = _.mapa(resultados, función(v, k) {
      v.valor.id = k;
      devolver v.valor;
    });
    res.render(cerveza/índice, {cervezas:cervezas});
  })
});

y la versión convertida:

db.consulta(
    "SELECT META().id AS id, * FROM muestra-cerveza WHERE type='cerveza' LIMIT " + ENTRADAS_POR_PÁGINA,
    función(err, cervezas) {
  res.render(cerveza/índice, {cervezas:cervezas});
});

Como puedes ver, ya no necesitamos hacer dos operaciones separadas para recuperar la lista. Podemos ejecutar nuestra consulta N1QL que nos devolverá toda la información que necesitamos, y le da el formato adecuado; en lugar de tener que reformatear los datos y añadir nuestros valores de id, podemos simplemente seleccionarlos como parte del conjunto de resultados. Encuentro que la versión N1QL es mucho más concisa y aprecio lo simple que fue construir la consulta.

A continuación, convertí la función de listado de cervecerías siguiendo un camino similar, y esto es lo que obtuve; como puede ver, es igual de bonito y conciso:

db.consulta(
    "SELECT META().id AS id, name FROM muestra-cerveza WHERE type='cervecería' LIMIT " + ENTRADAS_POR_PÁGINA,
    función(err, cervecerías) {
  res.render(cervecería/índice, {cervecerías:cervecerías});
});

A continuación convertí los métodos de búsqueda. Estos fueron un poco más de un desafío como mirando el código original directamente, sin pensar en lo que estaba tratando de lograr, la semántica no eran inmediatamente evidentes, he aquí un vistazo a lo que parecía:

var q = { tecla de inicio : valor,
  endkey : valor + JSON.analizar(‘”u0FFF"'),
  rancio : falso,
  límite : ENTRADAS_POR_PÁGINA }
db.ver( "cerveza", "by_name", q).consulta(función(err, valores) {
  var llaves = _.desplumar(valores, id);
  db.getMulti( llaves, null, función(err, resultados) {
    var cervezas = [];
    para(var k en resultados) {
      cervezas.pulse({
        id: k,
        nombre: resultados[k].valor.nombre,
        cervecería_id: resultados[k].valor.brewery_id
      });
    }
    res.enviar(cervezas);
  });
});

Una vez más, tenemos bastante código para conseguir algo que debería ser bastante sencillo. Por si no se da cuenta, la consulta map/reduce anterior recupera un listado de cervezas cuyos nombres empiezan por el valor introducido por el usuario. Vamos a convertir esto en una cláusula LIKE de N1QL y, como ventaja añadida, permitiremos que el término de búsqueda aparezca en cualquier parte de la cadena, en lugar de exigirlo al principio:

db.consulta(
    "SELECT META().id, name, brewery_id FROM muestra-cerveza WHERE type='cerveza' AND LOWER(nombre) LIKE '%" + plazo + "%' LÍMITE " + ENTRADAS_POR_PÁGINA,
    función(err, cervezas) {
  res.enviar(cervezas);
});

De nuevo hemos colapsado una gran cantidad de código vagamente comprensible hasta una consulta simple y concisa. Creo que esto comienza a mostrar el poder de N1QL y por qué estoy personalmente tan emocionado de ver N1QL. Sin embargo, hay una advertencia que noté al hacer esto, y es que al igual que con SQL, debes tener cuidado con el tipo de datos de usuario que pasas a tus consultas. Escribí una simple función de limpieza para tratar de evitar cualquier intención maliciosa (aunque N1QL es actualmente de sólo lectura de todos modos), pero mi código de limpieza no es en absoluto extenso. Otro problema que noté es que nuestra segunda consulta con la cláusula LIKE se ejecuta significativamente más lento como una consulta N1QL que cuando se utiliza map/reduce. Creo que esto es simplemente el resultado de que N1QL todavía es una versión preliminar para desarrolladores, y hay muchas optimizaciones por hacer por parte del equipo de N1QL.

Si quieres ver el código fuente completamente convertido, echa un vistazo a la rama n1ql del repositorio beersample-node disponible aquí, https://github.com/couchbaselabs/beersample-node/tree/n1ql.  

Gracias Brett

Autor

Publicado por Brett Lawson, Ingeniero de Software Principal, Couchbase

Brett Lawson es Ingeniero de Software Principal en Couchbase. Brett es responsable del diseño y desarrollo de los clientes Node.js y PHP de Couchbase, además de participar en el diseño y desarrollo de la biblioteca C, libcouchbase.

4 Comentarios

  1. Tengo muchas ganas de ver cómo se trabaja en esto

  2. Me temo que los ejemplos que utilizan la concatenación de cadenas para construir estas consultas empujarán a la gente hacia vulnerabilidades "n1ql-injection". De hecho, probablemente sería más seguro (y tal vez funcione así, no lo he comprobado) permitir la sustitución de variables en la consulta (por ejemplo, \ "%d\" o \ "$1\" o algo así), para que pueda asegurarse de que los valores se escapan.

    1. Hola Justin,
      Estoy completamente de acuerdo contigo, y en una versión posterior, introdujimos la parametrización y sustitución incorporadas (aunque actualmente no está disponible para el SDK 2.0). Esto es algo para lo que esperamos crear pronto una solución elegante. ¡Definitivamente antes de que N1QL sea GA! Gracias por tu aportación.
      Saludos, Brett

Dejar una respuesta