Caso real: Podcastvery.com -> Search_Api
El contenido central es resolver las típicas dudas que pueda tener alguien que tiene o quiere tener una web en Drupal.
Aparte de resolver dudas de "clientes", también se habla de tips, recomendaciones y buenas prácticas para el Developer que recién empiezan en este mundo.
Hoy te vengo a contar como se ha configurado el Search_Api, que problemas he tenido, que cosas he aprendido y que se puede hacer para mejorar el rendimiento en la indexación usando search_api
- Elasticsearch y el límite de 10k
- Integrar el módulo Flag con Search_Api
- Search Api y rendimiento, cloudtags
- Crear processors customs
- Migracion de categorías sin publicar. Modificar facets para evitar mostrarlas
Hola, bienvenido o bienvenida otra semana más aquí en Drupalizate. Sigo hablando de temas de podcastvery, de Drupal, de cómo está montado el proyecto podcastvery.com. Esta semana quiero hablar sobre datos y sobre lo que es Ser-Japi, cómo configurar y qué cosas, a menos para que te suenen, de cómo funciona Ser-Japi. Lo primero y todo, para que no sepas de qué va, escúchate el episodio pasado, de la semana pasada, que es de podcastvery, que habla un poco del resumen de qué es el proyecto y qué cosas he tenido en cuenta para este proyecto. Después, para quien no sepa lo que es un Ser-Japi, aunque lo he comentado ya en varios episodios, es el motor de búsqueda de un módulo contrib que está en Drupal, que te permite tener las búsquedas o indexar los datos que tienes en tu Drupal en un motor de búsquedas externo a tu base de datos. Lo puedes poner en un MySQL si es que quieres, que es lo que viene por defecto, o puedes activar, por ejemplo, un servidor en Solar o en Elasticsearch, que es en mi caso, y tener el índice de datos en Elasticsearch o en Solar, sobre todo por temas de rendimiento y también por temas de características técnicas, porque al tema de hacer facetados, digamos que te hace falta procesar los datos de alguna forma en la que no están originalmente en Drupal. Por eso es que en el momento de indexar en Ser-Japi ya se están indexando de la forma correcta para permitir después hacer búsquedas facetadas. Dicho todo esto, ¿qué problema es? Así me he hecho un pequeño guion para que no me deje nada porque ya me pasó con Babiso. El primer caso sería que el módulo de Elasticsearch Connector, que es el que uso para el backend de conectar Ser-Japi con Elasticsearch, tiene un bug que si tienes más de 10.000 resultados, digamos que en tu Drupal, que creas una views, configuras una views, y en el header de la views te muestra el número total de resultados. Estás viendo 10 de 200 o de tantos. Te da el cálculo total de cuántos ítems hay coincidentes para la búsqueda actual. Lo típico en Google de 10 millones de resultados coincidentes con tu búsqueda. En Drupal es muy simple de implementar esto. Basicamente en la views pones el header y le dices que te muestre el contador de resultados. Funciona muy bien. El problema es que con Elasticsearch si tienes más de 10.000 resultados te muestra que tienes 10.000 y en verdad no. En verdad tengo un millón y medio, no tengo 10.000. Y es porque hay un pequeño bug allí de que no devolvía la cantidad total, el número total de resultados disponibles. Como digo, si tú buscas Elasticsearch el bug de cantidad total de resultados, te sale de que hay un patch para eso. Lo comento porque ya hace tiempo que este bug se sabe que existe pero no está implementado, no está arreglado dentro del módulo principal de Elasticsearch Connector. Lo digo porque me pasó lo mismo en Babiso y me ha pasado lo mismo en podcastvery y es porque Serjapi me dice que tengo 10.000 ítems y es mentira. Dicho todo esto, ¿qué más me ha pasado con el tema de Serjapi en este proyecto? Esto me pasó entre comillas similar con WebCaster también. Para que no les suene, WebCaster es otro que ya comenté hace un par de semanas, otro proyecto, donde allí está jugando con el módulo Flags, donde cada nodo, o sea, cada usuario puede flaggear, puede votar un nodo de otro usuario. En ese caso, hay que pedir conexión. En el caso de podcastvery, cualquier usuario puede votar un podcast para decir si le gusta o no. Y los podcasts que tienen más votos, más likes, pues tendrán mayor peso en los resultados de búsqueda. Digamos que es una especie como de ProduHand, para quien... Buscas ProduHand.com es una web donde la gente puede votar por los proyectos. Pues similar. A final podcastvery es un listado de podcasts donde puedes votar por los podcasts que a ti te gustan y esos podcasts que la gente más ha votado saldrán recomendados para otra gente. El problema es que el módulo Flags no se integra con Serjapi. Así que, ¿cómo hago para que se muestren esos datos? O sea, ¿cómo detecto cuando alguien ha votado en un módulo con el Flags? Y, ¿cómo detecto esto en Serjapi? Tienes que instalar otro módulo, que es el Serjapi de Flags. El problema que tengo también con este es que no genera para Serjapi un campo del número total de votos. Te genera si el usuario lo ha votado o no. Pero yo quiero saber la cantidad total de votos que tiene ese nodo, ese podcast. Así que tuve que aplicar un parche de este módulo que lo que te implementa es cuando se indexa el nodo, calcula cuántos Flags, o sea, cuántos usuarios han flaggeado ese nodo, te da el total y esto es lo que se indexa en Serjapi. Y implementa también este módulo de que cada vez que alguien vota, que flaggea un nodo, ese nodo se marca como está pendiente de reindexación en Serjapi. Esto puede ser un problema si tienes muchísimos usuarios en la web, o sea, muchos usuarios concurrentes votando al mismo tiempo, porque si no tienes una tarea crón constantemente o has configurado y tienes un servidor potente de que cada vez que alguien vote se reindexe el nodo en el mismo instante, puedes tener un lag de que trae unos minutos en verse reflejado ese voto en el Asicset o en Serjapi y por tanto en los listados. Pero bueno, es una cosa a tener en cuenta, que si usas Flags y quieres que se meta en Serjapi, el módulo Flags por defecto no te sirve. Te sirve, lo tienes que tener, pero después tienes que instalar otros módulos contribuidos que te permiten meter esos datos en Serjapi y aparte si quieres tener un contador tienes que poner el parche que está disponible en tu parboraje. Si me acuerdo, que no sé yo, lo ponen en otras del programa. Pero bueno, igualmente, más que nada es que esto te suene que existen estos casos, porque no creo que mucha gente tenga un proyecto de este tipo de casuística, porque si tienes algún tipo de proyecto de NOS que quiero que los usuarios voten y que esto tenga dentro de buscador X cosas, que sepas que esto existe. Otros temas, y eso lo lozó también con los procesos que tenemos en Serjapi. Uno de los requisitos de este proyecto que me impuse a mí mismo es el tema de recomendaciones de podcasts similares en base al texto. Al final el podcast tenemos el título, el nombre del autor, la descripción del podcast y después para cada uno de sus episodios también tenemos título y descripción del episodio. Al final, juntando a todo tenemos mucho texto plano, o texto con HTML que yo limpio y lo dejo plano un poco, para que eso se indexe en Serjapi. ¿Cómo lo estoy indexando en Serjapi? ¿Cómo intento hacer que las recomendaciones tengan algo de sentido? Primero, en Serjapi tenemos lo que son los pesos. Puedes especificar que ciertos campos de texto tengan mayor peso que otros. Eso significa que por ejemplo, si alguien busca por mi nombre, Robert Minetrai, y mi nombre está en el campo título o en el campo autor del podcast, eso tiene mayor peso, o sea, ya lógico que tuviera mayor peso, que no si mi nombre está en la descripción de un episodio. Porque por ejemplo me han entrevistado en un podcast y lo han puesto en ese episodio de, hey, aquí entrevistamos a Robert Minetrai, y ya está. Cuando alguien busque mi buscador de podcast Verde, lo interesante es que salga primero el podcast que sea de mi autoría, porque el autor soy yo con mi nombre, o que en el título del podcast salga mi nombre. Y que si eso sea alguno que tenga un episodio, pues salga después. Esto es una de las potencias que tiene Serjapi que te permite configurar pesos en los campos de texto. Así que muy interesante. Dicho esto, el problema que tengo es que si en vez de buscar un nombre de persona, busco una palabra, digamos, mucho más genérica, como yo que sé, marketing o Drupal, o cosas de estas, ¿cómo puedo encontrar podcasts que hablen de esta temática? Porque posiblemente no estará en el título del podcast, o no estará en el nombre del autor, estará en episodios sueltos. ¿Cómo puedo recomendar, si estoy viendo un podcast en concreto, cómo puedo recomendar que haya otro podcast muy similar a este, que de 20 episodios en 5 se habla mucho de la temática Drupal? ¿Cómo puedo detectar esto y mostrarle contenido relevante al usuario? Aquí fue mi idea del tema de hacer nubes de tags. Para quien no sepa lo que son nubes de tags, digamos de forma simplista, que es calcular las veces que se repite una palabra en un texto, y las palabras que se repiten más se muestran más grande, que es lo típico de las nubes de tags, o, a menos que no seas un contador y sabes las palabras que más se repiten en ese texto. Yo uso eso para indexarlo en Setjapi, y le invito a que lo puse, no sé si a 30 o 40 palabras. Al final, cuando indexo en Setjapi, cojo las palabras que se repiten más, las que se repiten más de X veces, las 30 más repetidas las meto en Setjapi. Cuando estoy viendo un podcast, cojo también sus palabras que más se repiten, busco podcasts coincidentes con esas mismas palabras, o con algunas de las palabras, y así me permite mostrar contenido relevante similar al podcast que está viendo actualmente el usuario. Es una forma muy simple de obtener contenido relevante. De la misma forma, como sé que un usuario ha dado like a X Podcast, puedo obtener sus palabras más usadas en esos podcasts y recomendar podcasts similares que usen esas mismas palabras. Así que esto de usar los clout tags, que le he puesto yo el nombre así, o sea, las palabras más repetidas, yo creo que es una herramienta muy interesante para obtener resultados muy significativos en el tema de similitud de, en este caso de páginas, de nodos, de podcasts. ¿Cómo he hecho esto del cálculo de palabras más repetidas y cómo se indexa en Setjapi? Más que nada, que sepas que en Setjapi, en Drupal, hay una función que son los procesos de Setjapi. Básicamente son, digamos, campos falsos. A ver, al que ha tocado Setjapi le sonará que cuando vas a configurar un índice en Setjapi has de añadir campos de la entidad. Si es un nodo, pues te saldrá el id del nodo, el título, la descripción y los campos que tú tengas. Hay campos que Setjapi te permite añadir al índice que no son campos como tal de la entidad. Por ejemplo, más típico es el HTML, creo que lo digo de memoria, HTML o HTML renderizado. Básicamente te permite renderizar el nodo en el momento de indexación y le configuras de que te lo indexes con el ViewMode full o con el ViewMode teaser. De esta forma, en el momento de indexar ese nodo, Setjapi lo va a renderizar, o sea, va a obtener el HTML con todos los campos que hayas configurado en ese ViewMode y va a indexar todo lo que es HTML tal cual en Elasticsearch o en Solr o donde sea. Esto que está con HTML lo puedes hacer tú con código custom para lo que tú quieras. En mi caso lo he hecho con Cloutax, que básicamente es, hago una consulta a la base de datos, una query, muy simple, dame todas las descripciones de los episodios de este podcast que tiene esta idea. Con PHP cuento las palabras más repetidas, hago una eliminación de palabras de una lista negra porque hay muchas palabras que, por ejemplo, Google, Twitter, Amazon, comparte en Twitter, comparte en Facebook, sígueme en YouTube, son palabras muy repetidas, o episodios o podcasts son palabras muy repetidas, así que hay una lista negra para evitar ese tipo de palabras. Después limito, lo he dicho antes, a unas 30 palabras por podcast y esto se lo paso como que son las palabras más relevantes de este podcast y sus episodios y esto lo indexa Elasticsearch. Así que es una forma muy simple de hacer campos, entre comillas falsos, porque son campos que se calculan en el momento de la indexación del podcast. Dicho todo esto, también los procesos, he estado trazando con esto en este proyecto para el tema de rendimiento. Tenía un problema serio en el tema de indexación. Cuando en Setjapi tienes muchísimos nodos, como es mi caso que tengo un millón y medio de podcasts y después cada episodio del podcast es un nodo con una entity reference al podcast como individual, así que tengo muchísimos, tengo más de 20 millones de nodos en este Drupal. Es una burrada. Me tardaba muchísimo en indexar y eso es porque en Setjapi hay otra opción que tú puedes activar que es, lo digo de memoria, eran las entidad inversa o referencia inversa a la entidad o algo así, que básicamente se traduce en que tienes un nodo que es podcast, tienes un nodo que es episodio. El nodo episodio tiene un campo entity reference al podcast, que hace referencia del episodio al podcast. Pues Setjapi tiene una cosa que puedes activar que es detectar las relaciones inversas, con lo cual al indexar los podcasts detectará otros nodos que hacen referencia a este podcast y te permite indexar campos, por ejemplo los títulos o descripciones de cada uno de los episodios. Esto es lo que tenía yo al principio, porque solo configuración es muy fácil de configurar, dos checkbox y ya está. El problema de esto es que el cálculo para hacer todo esto es una burrada, es lento de cojones. Basicamente me tardaba casi tres días en indexar, o sea dejaba un comando de trash corriendo para indexar cuando tenía que reindexar todo de golpe, porque tenía que hacer algún cambio en el Setjapi. Me tardaba tres días en indexarlo, dos días y pico en indexarlo todo. Así que esto no es factible, cuando tienes tanto contenido activas la opción de entidades inversas, o sea de referencia de entidades inversas, no lo veo factible. ¿Cómo lo solucione yo? Lo solucione a medias, porque también tarda como un día aún, pero es menos de la mitad de lo que tardaba antes. En vez de usar este proceso que te viene por defecto, que es activar dos checkbox, creé un proceso propio que lo que hace es una query muy simple de solo los campos de texto que yo quiero. O sea, de esta forma, en vez de que Setjapi detecte que este nodo hace una referencia al podcast, vale, haz un nodo de todo el nodo y obténme este campo. No, no, no hace falta todo esto. Yo hago una query muy simple que es de esa columna de envase de datos, que es la columna que corresponde al campo de descripción de los episodios, un join con el campo donde se guarda el valor del entity reference del episodio que va al podcast, y básicamente es un filtro de, pues, el ID del podcast es este, los valores de las columnas de texto de episodios de la descripción, vale, todos son estos, vale, agregar, hacer un array merge, calcular por PHP las más repetidas y esto se lo mando a Setjapi. Con esa tontería que lo hice para los títulos de los episodios y para las descripciones de los episodios, gané, o sea, fue la mitad, o sea, el doble de rápido indexando en Setjapi. ¿Se puede optimizar mucho más? Seguro que sí, y también porque tengo otros campos que estoy indexando y, segunmente, por eso, va lento, vale, pero que sepas que cuando tienes muchísimos nodos, la indexación en Setjapi es muy lenta y depende de qué datos estés indexando, sobre todo si son referencias a entidades, la indexación es muy lenta y si te das por ingenier para optimizar esta indexación, pues, realmente se nota, vale. Después, ¿qué más quería comentar de esto? Ah, la migración y las categorías. Comentar por encima el tema de la migración, vale, porque en el episodio de la semana siguiente voy a comentar en más detalle el tema de la migración, pero aquí solo quería comentar que en el momento de importar datos, o sea, los podcasts y sus episodios, importo también categorías del podcast. Las categorías son taxonomías en Drupal, el problema es que en muchos podcasts han puesto lo que les ha salido de los cojones, ¿vale?, han puesto categorías mal, o sea, no están bien, o en idiomas que no toca o en palabras clave que están mal, o sea, tú las ves y dices eso está mal, o sea, poner dos guiones, eso no es una categoría, por ejemplo, o poner cosas en chino, eso no es una categoría, o sea, supuestamente según un Apple Podcast deberían estar en inglés, siempre las categorías deben ser en idiomas más inglés, y en muchos podcasts esto no lo cumplen. Bueno, dicho esto, ¿qué hago yo? En Drupal cojo la categoría del podcast, la creo como taxonomía dentro de Drupal y la creo como taxonomía no publicada. Desde no sé qué versión en Drupal 8, las taxonomías tienen estado de publicado y despublicado, igual que los notos, es una entidad que puedes publicar o despublicar. Así que las dejo por defecto despublicadas, después cada cierto tiempo, cuando yo me da la gana, entro como administrador, reviso por encima las categorías que tienen sentido que estén y las publico manualmente. Así, la siguiente vez que se indexe ese código, detectaré, ah, esta categoría es esta, y la asigno, y no la despubblico, la deja ya publicada. Entonces, una forma de tener contenido que no sabes si es 100% útil, lo creas dentro de Drupal, pero lo has despublicado, que ningún usuario lo puede ver. La ventaja de esto es que como son taxonomías y las taxonomías pueden tener jerarquía, yo puedo mover las taxonomías que se han quedado despublicadas y que estoy viendo que están mal, o sea que los nombres no son correspondientes, y las muevo como supi, o sea, como hijos, o sea, como taxonomías con jerarquía, o sea, como hijos a otra taxonomía que sí que está bien y que sí que está publicada. Por decir algo, no estoy metiendo sobre la marcha, pero si hay una categoría que es series y películas, y aquí me han puesto, yo que sé, una categoría que es series de Marvel, o Los Vengadores, quizá, Los Vengadores no quiero que esté como una categoría que la gente vea, pero si muevo esa categoría como un hijo de series y películas, cuando Seth Yappi indexe ese nodo con esa categoría, detectará que es una categoría hija de series y películas, y en los listados, cuando alguien filte por series y películas, como la categoría hija está incluida, me lo va a detectar y me los va a filtrar y me los va a mostrar bien. O sea, a finales, aunque las categorías estén despublicadas, Seth Yappi las puede detectar. Aquí viene el siguiente problema, y es que Seth Yappi, a menos a actualmente, las muestra igualmente. Aunque estén las taxonomías despublicadas, en el bloque de facetados, se sigue mostrando el label tal cual como si fuera público. Tuve que implementar un pequeño parche que básicamente comprueba si la taxonomía es pública o no y no la muestra en el bloque de facets. Fue una chorrada, pero es para que lo sepas. Mi forma de arreglar esto en la migración fue, me las quedas despublicadas, y las publico yo manualmente, y después en Seth Yappi me di cuenta de que salían todas y tuve que hacer un pequeño parche para que solo mostrase las correctas. Pero también, para que sepas que esto se puede hacer, que lo he hecho más de un proyecto, en Babiso, por ejemplo, hice lo mismo, aunque creo que tampoco lo comenté porque no me hice guion y me olvidé de comentarlo. Pero en Babiso muchas categorías están mal también, o no son relevantes, y al final es yo cada cierto tiempo, cada muchos meses, mejor dicho, porque en Babiso hace mucho que no entro. Reviso las categorías que se han ido creando nuevas, las que no son relevantes las muevo a categoría otros, y las pongo como hijas de otros, o las muevo a la categoría que es correspondiente, si es programación, si es, yo que sé, camarero, o lo que sea. Y eso Seth Yappi cuando lo indexa ya lo muestra correctamente en los filtrados. Vale, y qué más, creo que ya está básicamente, que ya llevo unos 20 minutos hablando. Que sepas que por Seth Yappi con tal volumen de datos y tal cantidad de nodos es lento, a menos si no vigilas cómo lo configuras. Que igualmente es lento en la indexación, después en el tema de búsquedas sí que se nota muchísimo que son mucho más rápidas que cualquier tipo de búsqueda que hagas en MySQL normal, así que si tienes una web con, digamos, intensiva en búsquedas o con muchos datos, es hiper recomendable no usar unas views de MySQL normales, sino indexar todo lo que puedas en un Seth Yappi con Solar o con Elasticsearch y usar esto para mostrar estos datos. También otra cosa a tener en cuenta, que esto no había comentado, es mejor que, a ver cómo digo, depende un poco del caso, pero Seth Yappi puede hacer de que te renderice la entidad, o sea, usas las views para que hagan los filtros en Seth Yappi, Seth Yappi te devuelva ya los resultados y views es la que renderiza esos datos en el frontend. Lo puedes hacer de dos formas, de que views te pinte los campos tal cual que vienen de Seth Yappi, o sea, digamos, los valores en crudo, con lo cual ya no estás haciendo las consultas a MySQL para que te obtengan los datos reales. Por poner un ejemplo, el valor en crudo de una taxonomía indexada en Seth Yappi es el ID de la taxonomía. En este caso no tiene ningún sentido que muestres los IDs porque la gente se va a quedar, ¿y esto qué es? Lo que se tiene que mostrar es el label o el link a la taxonomía, con lo cual cuando views obtiene los datos de Seth Yappi, lo que tiene que hacer views es, en vez de mostrar el ID, es mostrar el campo relacionado con eso, con lo cual va a hacer una o varias consultas a MySQL para obtener el label o la URL o lo que sea. Pero por otro lado, si lo que queremos mostrar es el título del nodo y ese mismo título tal cual está en Seth Yappi, pues no hace falta hacer ninguna query a MySQL, puedes mostrar tal cual los datos en crudo que has guardado en Seth Yappi. O de la misma forma, en vez de ir mostrando campo a campo y haciendo muchas consultas a MySQL, quizás mejor mostrar directamente el nodo renderizado en un view mode, por ejemplo el view mode teaser, y que lo que te devuelve Seth Yappi es el ID, básicamente los IDs coincidentes son estos. Y lo que hace views es para cada ID te renderiza la entidad con ese view mode correspondiente. Dependería, como digo, un poco de cada caso, pero hay casos que he hecho yo, por ejemplo, esto es de proyectos de clientes, pero usar views, o sea, Seth Yappi para hacer views en formato tabla, para comparativa de precios y de campos de características técnicas de productos, por rendimiento es mucho mejor que sean campos a poder ser que mostres tal cual lo que se indica en Seth Yappi, que no pases por el MySQL de Drupal. O sea que si eres un campo numérico, no tiene ningún sentido que lo que obtengas de Seth Yappi después vayas a MySQL para acabar pintando el mismo número que tenías en Seth Yappi, saltarte el paso intermedio de preguntar a MySQL y pintar directamente lo que te devuelva Seth Yappi. Lo digo porque en más de un proyecto he visto que por rendimiento se notaba muchísimo. En el caso en concreto de podcastvery, lo que hago es renderizar la entidad en formato, en view mode de teaser, y como esto después está cacheado porque los view modes se cachean en Drupal, a menos que hagas lo contrario, pero lo normal es que sí cachee todo, el rendimiento de la web, si ya navegas por la web verás que las buscas son muy rápidas. Y creo que ya está, que me he alargado mucho con esto. La semana que viene además enfacilizaré el tema de tamaño en disco y el tema de migraciones, de por qué hice una migración, por qué no uso el módulo Migrate, qué problemas he tenido y cómo estoy intentando solucionarlo, porque tengo un problema grave con el tema de disco y el tema de rendimiento. Así que hasta la semana que viene. Chao.
¿Tienes algún proyecto en mente?
Si quieres hacer algo en Drupal tal vez puedas contratarme.
Ya sea para consultoría, desarrollo o mantenimiento de sitios web Drupal.