Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Node.js

Crear un Buscador para tienda con Node.js y Redis

by
Difficulty:IntermediateLength:LongLanguages:

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

Visite el sitio web para cualquier cadena de restaurantes o tiendas y es probable que encuentre un "buscador de tienda": una pequeña página aparentemente simple donde ingresa su dirección postal o código postal y proporciona los lugares cerca de usted. Como cliente, es genial porque se puede encontrar lo que está cerca, y las implicaciones del negocio son obvias.

Construir un "buscador de tienda" es en realidad una tarea difícil. En este tutorial, cubriremos los conceptos básicos de cómo trabajar con datos geoespaciales en Node.js y Redis y construiremos un buscador rudimentario de tiendas.

Utilizaremos los comandos "geo" de Redis. Estos comandos se agregaron en la versión 3.2, por lo que tendrá que tener instalado en su máquina de desarrollo. Vamos a hacer un corto chequeo—enciende redis-cli y tipea GEOADD. Debería ver un mensaje de error similar a este:

A pesar del mensaje de error, es una buena señal—muestra que tiene el comando GEOADD. Si ejecuta el comando y obtiene el siguiente error:

Tendrá que descargar, crear e instalar una versión de Redis que admita los comandos geo antes de ir más lejos.

Ahora que tienes un servidor Redis compatible, vamos a hacer un recorrido por los comandos geo. Redis tiene seis comandos que están directamente involucrados en la indexación geoespacial: GEOADD, GEOHASH, GEOPOS, GEODIST, GEORADIUS y GEORADIUSBYMEMBER.

Empecemos con GEOADD. Este comando, como usted puede imaginar, agrega un elemento geoespacial. Tiene cuatro argumentos requeridos: clave, longitud, latitud y miembro. La clave es como un agrupamiento y representa un solo valor en el espacio de claves. Longitud y latitud son obviamente las coordenadas como flotadores; Tenga en cuenta el orden de estos valores, ya que es probable que se invierta de lo que está acostumbrado a ver. Por último, "miembro" es la forma en que va a identificar una ubicación. En redis-cli, vamos a ejecutar los siguientes comandos:

Esta es la forma a largo plazo de agregar entradas múltiples, pero es bueno ver el patrón. Si quisieras acortar este proceso, podrías lograr lo mismo repitiendo la longitud, la latitud y el miembro para cada lugar adicional como más argumentos. Este es un ejemplo de la representación a mano corta de los dos últimos ítems:

Internamente, estos elementos geográficos no son en realidad nada especial: son almacenados por Redis como un zset o conjunto ordenado. Para mostrar esto, vamos a ejecutar algunos comandos más en la llave va-universities:

Esto, devuelve zset, igual que cualquier otro conjunto ordenado. Ahora, ¿qué pasa si intentamos recuperar todos los valores e incluir las puntuaciones?

Esto devuelve una respuesta masiva de los miembros introducidos anteriormente, con un número muy grande—un entero de 52 bits. El entero es en realidad una representación de un geohash, una pequeña estructura inteligente que puede representar cualquier lugar del globo. Vamos a bucear un poco más profundamente más adelante y realmente no estarán interactuando con los datos geoespaciales de esta manera, pero siempre es bueno saber cómo se almacenan sus datos.

Ahora que tenemos algunos datos para jugar, veamos el comando GEODIST. Con este comando, puede determinar la distancia entre dos puntos que ha introducido previamente bajo la misma clave. Así que, vamos a encontrar la distancia entre los miembros virginia-tech y christopher-newport-university:

Esto debería producir 349054.2554687438, o la distancia entre los dos lugares en metros. También puede proporcionar un tercer argumento como una unidad mi (millas), km (kilómetros), pies (pies), o m (metros, el valor predeterminado). Vamos a obtener la distancia en millas:

Que debe responder con "216.89279795987412."

Antes de ir más lejos, vamos a hablar de por qué el cálculo de la distancia entre dos puntos geoespaciales no es sólo un simple cálculo geométrico. La tierra es redonda (o casi), de modo que al alejarse del ecuador, la distancia entre las líneas de longitud comienza a converger y "se encuentran" en los polos. Por lo tanto, para calcular la distancia, es necesario tener en cuenta el mundo.

Afortunadamente, Redis nos protege de esta matemática (si estás interesado, hay un ejemplo de una implementación de JavaScript puro). Una nota, Redis hace la suposición de que la tierra es una esfera perfecta (la fórmula de Haversine), y puede introducir un error de hasta el 0,5%, que es lo suficientemente bueno para la mayoría de las aplicaciones, especialmente para algo parecido a un buscador de tienda.

La mayor parte del tiempo vamos a querer todos los puntos dentro de un cierto radio de un lugar, no sólo la distancia entre dos puntos. Podemos hacer esto con el comando GEORADIUS. El comando GEORADIUS espera, al menos, la clave, la longitud, la latitud, la distancia y una unidad. Así que, vamos a encontrar todas las universidades en el conjunto de datos dentro de 100 millas de este punto.

Que devuelve:

GEORADIUS tiene algunas opciones. Digamos que queríamos obtener la distancia entre nuestro punto especificado y todas las ubicaciones. Podemos hacer esto añadiendo el argumento WITHDIST al final:

Esto devuelve una respuesta masiva con el miembro de ubicación y la distancia (en la unidad especificada):

Otro argumento opcional es WITHCOORD, que, como habrás podido adivinar, te devuelve las coordenadas de longitud y latitud. También puede mezclar esto con el argumento WITHDIST. Intentemos esto:

El conjunto de resultados se vuelve un poco más complicado:

Observe que la distancia viene antes de las coordenadas, a pesar del orden invertido en nuestros argumentos. Redis no le importa qué orden especifique el argumento WITH*, pero devolverá la distancia antes de las coordenadas. Hay uno más con argumentos (WITHHASH), pero lo cubriremos en una sección posterior—sólo sé que vendrá en último lugar en tu respuesta.

Un pequeño comentario sobre los cálculos que se están llevando a cabo aquí—Si piensas en las matemáticas que ya cubrimos en cómo funciona GEODIST, pensemos en un radio. Puesto que un radio es un círculo, tenemos que pensar en un círculo que se coloca sobre una esfera, que es muy diferente de un simple círculo aplicado sobre un plano llano. Una vez más, Redis hace todos estos cálculos para nosotros (afortunadamente).

Ahora, vamos a cubrir un comando relacionado a GEORADIUS, GEORADIUSBYMEMBER. GEORADIUSBYMEMBER funciona exactamente igual que el GEORADIUS, pero en lugar de especificar una longitud y una latitud en los argumentos, puede especificar un miembro ya en su clave. Así, por ejemplo, esto devolverá a todos los miembros dentro de 100 millas dela miembro university-of-virginia.

Puede utilizar las mismas unidades y WITH* argumentos y unidades en GEORADIUSBYMEMBER como podría hacerlo en GEORADIUS.

Anteriormente, cuando ejecutamos ZRANGE en nuestra clave, es posible que se haya preguntado cómo recuperar las coordenadas de una posición que agregó con GEOADD, lo podemos lograr con el comando GEOPOS. Al suministrar la llave y un miembro, podemos recuperar las coordenadas:

Que debe producir un resultado de:

Si nos fijamos en cuando se agregó el valor de university-of-virginia, los números son un poco diferentes, aunque redondean a la misma cantidad. Esto se debe a cómo Redis almacena las coordenadas en el formato geohash. Una vez más, esto es muy cercano y lo suficientemente bueno para la mayoría de las aplicaciones—en el ejemplo anterior, la distancia real diferencia entre la entrada y la salida de GEOPOS es de 5,5 pulgadas / 14 cm.

Esto nos lleva a nuestro comando Redis GEO final: GEOHASH. Esto devolverá el valor geohash utilizado para mantener las coordenadas. Mencionado anteriormente, este es un sistema inteligente que se basa en una rejilla y puede ser representado en una variedad de maneras—Redis utiliza un entero de 52 bits, pero una representación más comúnmente vista es una cadena de base-32. Utilizando el comando GEOHASH con la clave y un miembro, Redis devolverá la cadena base-32 que representa esta ubicación. Si ejecutamos el comando:

Obtendras:

Esta es la representación de la cadena geohash base-32. Las cadenas de Geohash tienen una propiedad limpia que si quita caracteres de la derecha de la cadena, reduce progresivamente la precisión de las coordenadas. Esto puede ilustrarse con el sitio web de geohash: mire estos enlaces y vea cómo las coordenadas y el mapa se alejan de la ubicación original:

Hay una función más que necesitaremos cubrir, y si ya estás familiarizado con los conjuntos clasificados de Redis ya lo sabes. Dado que sus datos geoespaciales sólo se almacenan en un zset, podemos eliminar un elemento con ZREM:

Servidor del Buscador de tienda

Ahora que tenemos los fundamentos abajo para usar los comandos de Redis GEO, construyamos buscador de tienda basado en Node.js como ejemplo en servido. Vamos a utilizar los datos de arriba, así que supongo que esto es técnicamente un buscador de universidades en lugar de un buscador de tiendas, pero el concepto es idéntico. Antes de empezar, asegúrese de tener tanto Node.js como npm instalados. Haga un directorio para su proyecto y cambie a ese directorio en su línea de comandos. En la línea de comandos, escriba:

Esto creará su archivo package.json haciéndole algunas preguntas. Después de haber inicializado su proyecto, instalaremos cuatro módulos. De nuevo, desde la línea de comandos, ejecute los siguientes cuatro comandos:

El primer módulo es Express.js, un módulo de servidor web. Para ir junto con el servidor, también tendremos que instalar un sistema de plantillas. Para este proyecto usaremos pug (conocido formalmente como Jade). Pug se integra muy bien con Express y nos permitirá crear una plantilla de página básica en sólo unas pocas líneas. También instalamos node_redis, que gestiona la conexión entre Node.js y el servidor Redis. Por último, necesitaremos otro módulo para manejar la interpretación de los valores HTTP POST: body-parser.

Para nuestro primer paso, sólo vamos a levantar el servidor hasta el punto de que puede aceptar peticiones HTTP y rellenar la plantilla con valores.

Este servidor solo servirá con éxito la página de nivel superior ('/') y sólo si el cliente HTTP (a.k.a. navegador) solicita con un método GET o POST.

Vamos a necesitar una plantilla de huesos, lo suficiente para poder mostrar un encabezado, el formulario y (más adelante) mostrar los resultados. El barro amasado es un lenguaje templating muy terso con espacios en blanco relevantes. Por lo tanto, con la indentación de etiquetas de anidación, la primera palabra de una línea después de la indentación es la etiqueta (y las etiquetas de cierre son inferidas por el analizador) y estamos interpolando los valores con #{}. Esto requiere cierto tiempo para acostumbrarse, pero puede crear una gran cantidad de HTML con caracteres mínimos: eche un vistazo al sitio web de pug para obtener más información. Nota al momento de este artículo, el sitio web oficial de Pug no ha sido actualizado. . Aquí está el boleto oficial de GitHub con respecto al problema

Podemos probar nuestro buscador de tienda iniciando el servidor en la línea de comandos:

Luego ingrese en su navegador ahttp://localhost:3000/.

Debería ver una página sencilla, sin trama, con un encabezado grande que dice "University Finder" y un formulario con un par de cuadros de texto, Ya que una solicitud de página normal por un navegador es una solicitud GET, esta página está siendo generada por la función en El argumento para app.get.

Basic form screenshot

Si introduce valores en los libros de texto de Latitud y Longitud y hace clic en "Buscar", verá que esos resultados se representan y se muestran en la línea que dice "Mostrar resultados para ..." En este punto, no obtendrá ningún resultado, Como todavía no hemos integrado Redis.

Form with values after click screenshot

Integración de Redis

Para integrar Redis, primero tendremos que hacer una pequeña configuración. En la declaración de variables, incluya tanto el módulo como una variable (aún no definida) para el cliente.

Después de la declaración de variables, tendremos que crear la conexión a Redis. En nuestro ejemplo, asumiremos una conexión localhost en el puerto predeterminado y sin autenticación (en un entorno de producción, asegúrese de proteger su servidor Redis).

Una característica interesante de node_redis es que el cliente hará cola de comandos mientras se establece una conexión, por lo que no hay necesidad de preocuparse por esperar para establecer una conexión con el servidor Redis.

Ahora que nuestra instancia de nodo tiene un cliente Redis que puede aceptar conexiones, vamos a trabajar en el corazón de nuestro buscador de tiendas. Tomaremos la latitud y longitud del usuario y lo aplicaremos al comando GEORADIUS. Nuestro ejemplo es usar un radio de 100 millas. También vamos a querer obtener la distancia y las coordenadas de esos resultados.

En la devolución de llamada, manejamos cualquier error, si surge. Si no se encuentran errores, muestre los resultados para que sean más significativos y más fáciles de integrar en la plantilla. Estos resultados se introducen en la plantilla.

En la plantilla, necesitamos manejar el conjunto de resultados. Pug tiene iteración sin fisuras sobre matrices (con una sintaxis casi verbal). Se trata de extraer esos valores para un solo resultado; La plantilla se encargará de todo lo demás.

Después de que tenga su plantilla final y código de nodo en su lugar, reinicie su servidor app.js y apunte su navegador de nuevo a http://localhost:3000/.

Si introduce una latitud de 38.904722 y una longitud de -77.016389 (las coordenadas de Washington, DC, en la frontera norte de Virginia) en los cuadros y haga clic en buscar, obtendrá tres resultados. Si cambia los valores a una latitud de 37.533333 y una longitud de -77.466667 (Richmond, Virginia, la capital del estado y en la parte central / oriental del estado), verá diez resultados.

En este punto, usted tiene las partes básicas de un buscador de tienda, pero tendrá que ajustarlo para adaptarse a su propio proyecto.

  •     La mayoría de los usuarios no piensa en términos de coordenadas, por lo que tendrá que considerar un enfoque más fácil de usar, tales como:
    1. Uso del JavaScript del cliente para detectar la ubicación mediante la API de geolocalización
    2. Uso de un servicio geolocalizador basado en IP
    3. Solicite al usuario un código postal o dirección y utilice un servicio de geocodificación que se convierta en coordenadas. Muchos servicios de geocodificación diferentes están en el mercado, así que escoja uno que funcione bien para su área de destino.

  • Este script no realiza validación de formularios. Si deja los cuadros de entrada de latitud y longitud, debe asegurarse de que está validando sus datos y evitando un mensaje de error.

  • Expanda la clave de ubicación en información más útil. Si está utilizando Redis para almacenar más información sobre cada ubicación, considere almacenar esa información en hashes con una clave que coincida con los miembros devueltos de GEORADIUS. Tendrá que hacer llamadas adicionales a Redis.

  • Más estrechamente integrar con un servicio de cartografía como Google Maps, OpenStreetMap o Bing Maps para proporcionar mapas integrados y direcciones.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.