Ingeniería

Cómo crear búsqueda de aplicaciones con React y Elastic App Search

Cuando un usuario busca algo, quiere obtener resultados relevantes. Pero los resultados relevantes son solo una parte de lo que convencerá a un visitante, su búsqueda también debe sentirse bien. La búsqueda debe ser rápida, reaccionar a la entrada y sentirse inteligente y efectiva.

En este tutorial aprenderás sobre la búsqueda de aplicaciones a través de una demostración sobre cómo crear una experiencia de búsqueda robusta y fluida con React y el cliente JavaScript de Elastic App Search. Al finalizar, tendrás una aplicación React-ificada, relevante y atractiva que te permitirá buscar en varios paquetes npm en tiempo real mediante clasificación por facetas y el estado mantenido como parte de la URI.

El código completo puede encontrarse en GitHub.


¡Oye!

Tenemos un nuevo artículo sobre cómo crear una búsqueda estupenda incluso más rápido con Search UI, una biblioteca open source de Elastic.

Lee ese artículo en lugar de este.


Requisitos

Para proceder, necesitarás lo siguiente…

  1. Una versión reciente de Node.js
  2. Una versión reciente de npm
  3. Una cuenta de Elastic App Search Service o una prueba gratuita de 14 días activa
  4. Unos 30 minutos

App Search, una introducción

Las aplicaciones se desarrollan en torno a los datos. Facebook explotó en nuestros círculos sociales gracias a que presentaba los datos de "amigos" de forma interesante. eBay comenzó siendo la forma más optimizada de encontrar y comprar productos usados. Wikipedia hizo que sea fácil para los lectores aprender sobre… bueno, ¡todo!

Las aplicaciones existen para resolver problemas de datos. En este esfuerzo, la búsqueda es una compañera esencial. Si la aplicación es grande, una parte clave de su oferta será la búsqueda: encontrar amigos, productos, conversaciones o artículos. Mientras más grande e interesante sea el set de datos, más popular será la aplicación; en especial si la búsqueda es relevante y gratificante.

Elastic App Search está desarrollado a partir de Elasticsearch, un motor de búsqueda RESTful distribuido y open source. Con Elastic App Search, los desarrolladores reciben acceso a un set robusto de endpoints de API optimizados para encargarse de casos de uso de búsqueda de aplicaciones prémium.

Ponte en marcha

Para comenzar, crea un motor dentro de App Search.

Un motor ingesta objetos para indexación. Un objeto son los datos; el perfil de un amigo, el producto o la página de la Wiki. Una vez que los datos se ingestaron en App Search, se indexan respecto a un esquema flexible y se optimizan para la búsqueda. A partir de allí, podemos aprovechar distintas bibliotecas cliente a fin de elaborar una experiencia de búsqueda agradable.

A los fines de este ejemplo, llamaremos a nuestro motor: node-modules.

Una vez que el motor se creó, necesitaremos tres datos de la página Credentials (Credenciales):

  1. El Host Identifier (Identificador del host), con el prefijo host-
  2. Una Private API Key (Clave de API privada), con el prefijo private-
  3. Una Public Search Key (Clave de búsqueda pública), con el prefijo search-

Con esto, podemos clonar el proyecto, ingresar al directorio, ver la rama "iniciador" y luego ejecutar una instalación npm:

$ git clone https://github.com/swiftype/app-search-demo-react.git
$ cd react-tutorial && git checkout starter && npm install

Excelente; tenemos una aplicación preparada y lista. Pero para buscar, se necesitan datos…

Ingesta ~

En la mayoría de los casos, los objetos residirán en una base de datos o una API de backend. Como esto es un ejemplo, usaremos un archivo .json estático. El repositorio contiene dos scripts: init-data.js e index-data.js. El primero es un raspador que se usó para adquirir datos de node-module bien formateados a partir de npm. Los datos existen en el archivo node-modules.json. El segundo es un indexador que ingestará esos datos en tu App Search Engine para indexación.

A fin de ejecutar el script indexador, tendremos que pasar nuestro Host Identifier (Identificador de host) y Private API Key (Clave de API privada) junto con él.

$ REACT_APP_HOST_IDENTIFIER={Your Host Identifier} \
REACT_APP_API_KEY={Your Private API Key} \
npm run index-data

Los objetos se envían rápidamente a App Search Engine en lotes de 100 y el índice se crea.

Ahora deberíamos tener un Dashboard para nuestro recientemente creado motor con ~9500 paquetes de npm indexados como documentos. Puede ser útil explorar los datos para familiarizarnos con el contenido.

app_search_engine_overview.png

Reactividad

Con nuestro motor completo y listo, podemos comenzar a crear la aplicación central.

$ npm start

Iniciar npm desde adentro del directorio del proyecto abrirá el modelo de React. Toma el estilo de App.css; nos permite personalizarlo mejor para que se adapte a nuestras necesidades.

En el futuro cercano, necesitaremos un cuadro de búsqueda en el que podamos escribir nuestras consultas de búsqueda. Los usuarios buscarán estos rectángulos útiles porque los motores de búsqueda y los navegadores ya los entrenaron bien: escribe aquí y encuentra lo que buscas.

//App.css
...
.App-search-box {
height: 40px;
width: 500px;
font-size: 1em;
margin-top: 10px;
}

También tendremos que poner nuestras credenciales de acceso de App Search en un lugar seguro, como un archivo .env.

Crea uno en el directorio raíz del proyecto y complétalo, así:

//.env
REACT_APP_HOST_IDENTIFIER={your host identifier, prefixed with host-}
REACT_APP_SEARCH_KEY={your public search key, prefixed with search-}

Con las variables guardadas de forma segura, podemos comenzar a escribir nuestra lógica de búsqueda.

Comenzar a buscar

El archivo App.js es donde residirá la lógica central. Este archivo (junto con la mayoría de los demás archivos iniciadores) se creó con create-react-app, una herramienta para ayudar a iniciar aplicaciones React sin ninguna configuración. Antes de escribir cierta lógica para probar la búsqueda, debemos instalar la biblioteca de cliente JavaScript de Swiftype App Search:

$ npm install --save swiftype-app-search-javascript

Coloca el siguiente código en App.js. Realizará una búsqueda básica.

Codificaremos de forma rígida foo como nuestro término de búsqueda de ejemplo:

import * as SwiftypeAppSearch from "swiftype-app-search-javascript";
const client = SwiftypeAppSearch.createClient({
hostIdentifier: process.env.REACT_APP_HOST_IDENTIFIER,
apiKey: process.env.REACT_APP_SEARCH_KEY,
engineName: "node-modules"
});
//Podemos buscar cualquier cosa -- foo es nuestro ejemplo.
const query = "foo";
const options = {};
client.search(query, options)
.then(resultList => console.log(resultList))
.catch(error => console.log(error))

El navegador se actualizará y creará una matriz resultList a través de console.log. Para explorar la matriz, podemos abrir la consola del desarrollador de nuestro navegador. Podemos probar algunas búsquedas más reemplazando la búsqueda foo con otro texto. Una vez cambiada la búsqueda y actualizada la página, podemos ver cómo se adaptó el resultado.

Excelente ~ con eso, ya estamos buscando en nuestro node-modules.

Beneficios resultantes

Tenemos un patrón más simple para buscar, pero los resultados no son muy útiles si están ocultos en un console.log. Quitaremos el estilo de React básico y nuestro código anterior, luego extenderemos las cosas.

Crearemos…

  1. Una variable de estado que tendrá una propiedad de respuesta.
  2. Un método performQuery que buscará en App Search usando client.search. Almacenará los resultados de búsqueda dentro de la propiedad de respuesta.
  3. Un hook de ciclo de vida componentDidMount, que se ejecutará una vez en la carga de la aplicación. Buscaremos foo nuevamente, pero podemos usar cualquier búsqueda que queramos.
  4. HTML estructurado para que mantenga la salida de datos resultante y la cantidad total de resultados.
//App.js
// ... Truncado
class App extends Component {
state = {
// Una nueva propiedad de estado, que mantiene la respuesta de búsqueda más reciente
response: null
};
componentDidMount() {
/*Llamar esto en componentDidMount asegura que los resultados se muestren en
la pantalla cuando la app se cargue por primera vez*/
this.performQuery("foo");
}
// Método para realizar una búsqueda y almacenar la respuesta
performQuery = queryString => {
client.search(queryString, {}).then(
response => {
// Agrega esto por ahora para poder inspeccionar la respuesta completa
console.log(response);
this.setState({ response });
},
error => {
console.log(`error: ${error}`);
}
);
};
render() {
const {response} = this.state;
if (!response) return null;
return (


Node Module Search



{/*Muestra el recuento total de resultados de esta búsqueda*/}

{response.info.meta.page.total_results} Results


{/*Itera por los resultados y muestra su nombre y descripción*/}
{response.results.map(result => (

Name: {result.getRaw("name")}


Description: {result.getRaw("description")}



))}

);
}
}
// ... Truncado

En el momento en que hacemos clic en guardar, los resultados aparecerán en http://localhost:3000 — 27 resultados y algunos módulos interesantes. Si algo salió mal, podemos comprobar la consola, dado que tenemos dos console.logs anidados en el código.

Cuadros sofisticados

Codificamos de forma rígida foo en nuestras búsquedas. Lo que hace que la búsqueda sea más valiosa es que comienza con una expresión libre. Una vez que desarrollaste una experiencia de búsqueda estupenda, podrás optimizar para expresiones más comunes, adaptando los sets de resultados más relevantes. Todo comienza con un lienzo en blanco: el cuadro de búsqueda.

Para crear un cuadro de búsqueda capaz, agregaremos una propiedad al estado denominada queryString. A fin de mantener queryString actualizada con los textos nuevos, crearemos un método updateQuery; aprovecharemos el controlador onChange para actualizar queryString y desencadenaremos una nueva búsqueda cada vez que un usuario cambie el texto en el cuadro.

Nuestra clase de App completa ahora se ve así:

//src/App.js
// ... Truncado
class App extends Component {
state = {
// Una propiedad de estado nuevo, que rastrea el valor a partir del cuadro de búsqueda
queryString: "",
response: null
};
componentDidMount() {
// Elimina la búsqueda codificada de forma rígida de "node"
this.performQuery(this.state.queryString);
}
// Se ocupa del evento onChange cada vez que el usuario escribe en el cuadro de búsqueda.
updateQuery = e => {
const queryString = e.target.value;
this.setState(
{
queryString // Guarda el texto de búsqueda que ingresó el usuario
},
() => {
this.performQuery(queryString); // Desencadena una búsqueda nueva
}
);
};
performQuery = queryString => {
client.search(queryString, {}).then(
response => {
this.setState({
response
});
},
error => {
console.log(`error: ${error}`);
}
);
};
render() {
const {response, queryString} = this.state;
if (!response) return null;
return (


Node Module Search



{/*Un cuadro de búsqueda, conectado a nuestro valor de texto de búsqueda y controlador
onChange*/}

);
}
}
// ... Truncado

Debounce

Dentro de esta iteración, cada vez que se detecta un cambio dentro del cuadro, se realizará una búsqueda; esto puede tornarse intensivo para los sistemas. A fin de corregirlo, aplicaremos una función _debounce_, cortesía de Lodash.

$ npm install --save lodash

Debounce es un método de limitación de la velocidad de la cantidad de solicitudes entrantes conforme a una cantidad definida de milésimas de segundos. Cuando un usuario piensa sobre cómo redactar la búsqueda, comete errores tipográficos o escribe muy rápido, no necesitamos buscar con cada cambio detectado.

Al incluir nuestro método performQuery en una función debounce de Lodash, podemos especificar un límite de velocidad de 200 ms; deben pasar 200 ms sin que se realice una entrada para que comience la próxima consulta de búsqueda:

//App.js
// ... Truncado
import { debounce } from "lodash"; // Importa debounce
// ... Truncado
performQuery = debounce(queryString => {
client.search(queryString, {}).then(
response => {
this.setState({
response
});
},
error => {
console.log(`error: ${error}`);
}
);
}, 200); // 200 milliseconds.
// ... Truncado

Además de darle un respiro a nuestros servidores, limitar la velocidad puede ayudar a que las búsquedas de los usuarios sean más fluidas. ¡Ayuda mucho! Se siente importante.

A continuación…

Es el inicio de una experiencia de búsqueda React-ificada de calidad. En adelante, hay muchas cosas estupendas para agregar. Podríamos agregar estilo o implementar características de App Search dinámicas, como facetas, curaciones o ajuste de relevancia. O podríamos explorar el Conjunto de API de analíticas para descubrir información valiosa sobre la actividad de búsqueda de los usuarios.

Si quisiéramos profundizar mucho, en el archivo README dentro de la rama maestra se amplía el tutorial para crear la gestión de estados basada en URI, agregar estilos eficientes, paginación, filtrado y búsqueda facetada. Con algunas personalizaciones de estilo, puede convertirse en la base de una experiencia de búsqueda de alta calidad.

Resumen

Antes, crear una búsqueda de aplicaciones relevante y gratificante era complicado. Elastic App Search es una forma gestionada y conveniente de escribir una búsqueda valiosa y adaptable en las aplicaciones web. ¿Lo mejor? Tanto los ingenieros como las partes interesadas con menos conocimiento técnico pueden gestionar características clave desde un dashboard eficiente e intuitivo. Puedes comenzar directamente con App Search con una prueba gratuita de 14 días; no se requiere tarjeta de crédito.