jueves, 15 de septiembre de 2011

Lucene: sin regla ni Compass

Saludos después de una larga pausa donde, básicamente, me he dedicado a la familia y al ocio, para que negarlo. La verdad es que no estaba seguro de si volvería a escribir o no en el blog, ya que mi visión de eso llamado comunidad se va degradando con el tiempo, pero los ánimos me han dado para algunas entradas más, y aquí estamos de nuevo.

Esta entrada debería haber sido algo diferente, ya que mi intención era principalmente comentar la librería Compass, que es una especie de envoltorio de Lucene para facilitar su uso cuando tratamos con objetos Java, pero después de usarla en unos cuantos proyectos y estar relativamente satisfecho con su uso, un ligero problemilla me hizo consultar sus listas de distribución donde buceando entre el spam me encontré con la sorpresa de que su autor declaraba oficialmente que pasaba a hacer otra cosa y que ya no le interesaba trabajar en la librería. Al ser un one-man project, la cosa queda en que Compass es, en estos momentos, “abandonware”, por lo que hacer entradas explicando su uso, ventajas… me parece hacerle un flaco favor al pobre programador que debido a ellas acabe usándola y encontrándose la misma desagradable sorpresa que yo.
Sin embargo, aparte de advertir del estado del Compass, he decido modificar el contenido de la entrada y limitarme a hablar de los conceptos que he aprendido últimamente del Lucene y que son aplicables fuera de Compass.

Así que si alguna vez te toca trabajar con Lucene, espero que estos consejillos te sirvan:
  • Lo más normal, si estas realizando búsquedas en castellano o algún lenguaje con caracteres "raros" para los anglos, es que quieras usar un analizador personalizado (custom analyzer) que utilice, al menos, los filtros ISOLatin1AccentFilter, LowerCaseFilter y StandardFilter. De esta forma tus documentos (así es como llama Lucene a lo que sea que indexes) se indexarán sin tener en cuenta mayúsculas, ni acentos u otros caracteres no-ascii.
  • Eso sí, una vez indexados los documentos de esa forma, ojo con una característica del Lucene muy desagradable: Las búsquedas se deben pasar, habitualmente, por el mismo analizador que al indexar, pero el Lucene ignora al analizador sin decir nada si en las búsquedas se usan comodines (‘*’ o ‘?’). Así que si realizamos una búsqueda sin comodines, los resultados no serán independientes de las mayúsculas o los acentos y nos llevaremos desagradables sorpresas. La excusa oficial, con su pequeña parte lógica, es que el analizador puede cambiar los términos de búsqueda, por ejemplo para buscar  en singular y plural indistintamente, y entonces al hacer esos cambios junto a comodines, el termino final de búsqueda podría no parecerse a lo que quería el usuario. Bueno, vale. ¡Pero déjame elegir! En nuestro caso no puede ocurrir nada de eso, simplemente cambiamos letras mayúsculas por minúsculas y cambiamos á por a … pero Lucene considera que dejar escoger al programador, pobre tonto, es un peligro y no da la opción de desactivar esa “ayudita”. Sin comentarios. La única solución que he encontrado es pasar por esos mismo filtros los términos de búsqueda “manualmente” y antes de pasárselos al Lucene, lo cual me parece una chapuza. Pero avisados quedáis.
  • Otro truco útil es para cuando la gente quiere hacer una búsqueda de palabras que “acaben en”, o sea *algo . Lucene está pensado para búsquedas que “empiecen por”, o sea algo*, y en cambio empezar por un comodín es muy ineficiente. ¿Como solventarlo? Muy sencillo, a la hora de indexar, indexamos ese campo al revés y a la hora de buscar le damos la vuelta al termino de búsqueda, o sea ogla*. Por el módico precio de tener indexado el campo dos veces, volvemos a tener búsquedas eficientes: voilà.
  • Este último caso es un claro ejemplo de una técnica general muy útil cuando trabajamos con búsquedas indexadas, lo que hacemos con Lucene, vaya: En caso de tener problemas al montar el criterio de búsqueda, se puede probar a modificar la forma en que se indexa el contenido para facilitar las cosas. Por ejemplo, tenemos una lista de productos que pueden venderse en distintas tiendas y de esas tiendas hay sucursales en distintas provincias. Si a la hora de indexar, usamos un campo para indexar la lista de tiendas en las que se vende un producto y en otro campo la lista de provincias donde se vende… ¿Cómo podemos saber en qué provincias se vende el producto X en una tienda determinada? La respuesta es que así indexado no lo podemos saber, ya que hemos perdido la relación tienda-provincia. En este caso lo que tenemos que hacer es crear un nuevo campo donde indexemos el par tienda-provincia, que es por el que buscaremos para este tipo de consultas.
  • De igual forma, podemos indexar en varios campos los apellidos junto con el nombre o los apellidos por separado, según como queramos poder buscarlos. A no ser que tengamos un índice de modificaciones muy alto, o un volumen de datos inmenso, no hay que tener miedo en invertir en la indexación a cambio de obtener un rendimiento mucho más alto en la parte presumiblemente más usada: las búsquedas.

Happy coding! EJ

sábado, 2 de julio de 2011

Generando números de versión automáticamente con Maven

En esta entrada de hoy mostraré como implementar un sistema para que los usuarios de nuestras librerías puedan averiguar fácilmente cual es la versión que están utilizando y que se mantenga automáticamente al generar nuestros .jar con Maven.

Introducción
Uno de los “problemas/features” cuando uno usa Maven/Ivy/Grape o sistemas similares para gestionar sus dependencias es que independientemente de la versión, las librerías acaban teniendo un nombre común. Es decir, que la librería org.hibernate:hibernate:3.2.6.ga acaba llamandose hibernate.jar, igual que si fuera la 3.3.0.SP1 o cualquier otra versión. Esta característica ayuda a la hora de reemplazar una versión con otra, pero dificulta averiguar de un vistazo que versiones de librería estamos utilizando.

En caso de que nosotros hagamos una librería y se use a través de uno de estos sistemas, nuestros usuarios tendrán el mismo problema para averiguar en el sistema final qué versión está desplegada. Como somos unos “grandes” programadores, queremos facilitar a nuestros usuarios el averiguar está información, pero como somos unos programadores vagos eficientes no queremos tener que hacerlo a mano cada vez que generamos un .jar, y menos si usamos algo como Maven para gestionar nuestro proyecto. Así pues, ¿como lo podemos hacer?

Implementación
Paso 1:
Lo primero que tenemos que hacer es almacenar el número de versión de forma automática en algún sitio. Un buen sitio para hacerlo es en el fichero Manifest de nuestro .jar, y para hacerlo de forma automática podemos utilizar el plugin de Maven org.codehaus.mojo.buildnumber-maven-plugin. La documentación que tiene no es demasiado extensa, pero investigando un poco podemos averiguar cómo usarlo. En mi caso, dado que utilizo Mercurial como sistema de versiones y el numero de versión que utiliza no es muy significativo (no es un número correlativo) así que lo he sustituido por una marca de tiempo que me da una indicación más fiable. Si usas algo como Subversion, entonces quizá la configuración estándar del plugin ya te sirva Dado que el plugin no se encuentra en el repositorio central de Maven, tenemos que añadir un repositorio de plugins para encontrarlo, de la siguiente forma:
    
        codehaus-snapshot
        Cohehaus snapshot repository
        https://nexus.codehaus.org/content/groups/snapshots-group
        
          true
        
    

Una vez hecho esto, configuramos el plugin para que nos defina unas variables con la información que queremos, así:
    
      org.codehaus.mojo
      buildnumber-maven-plugin
      1.0-beta-5-SNAPSHOT
      
        
          validate        
          
            create
          
        
      
      
        true
        true
        {0,date,dd/MM/yyyy HH:mm:ss}
        
          timestamp
        
      
    


Con esto le decimos que nos añada el timestamp a la variable buildNumber, que es donde el plugin pone su información.
Por último, configuramos el plugin org.apache.maven.plugins.maven-jar-plugin para que use esa información para añadir una nueva entrada en el Manifest de nuestro jar. De la siguiente forma:


 org.apache.maven.plugins
 maven-jar-plugin
 2.3.1      
 
  
   false
   
    ${version}-${buildNumber}
    my.package.MainApp
   
  
 



Una vez hecho esto, si ejecutamos mvn package, por ejemplo, para generar nuestro fichero ,jar y miramos el fichero MANIFEST.MF dentro del directorio META-INF, deberíamos ver algo tal que así:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: usuario
Build-Jdk: 1.6.0_21
Main-Class:  my.package.MainApp
MyLibrary-version: 0.1-SNAPSHOT-25/06/2011 13:33:01

Paso 2:
Bien, ya tenemos almacenada la versión en el .jar. ¿Ahora que hacemos para facilitar que el usuario pueda ver esa información sin tener que abrir el .jar y mirar el manifest? Muy sencillo: la clase my.package.MainApp que es la principal de nuestra aplicación tiene que mostrar esa versión. En mi caso, la librería es una utilidad que no se lanza en linea de comandos, se usa añadiéndola al classpath, así que mi clase MainApp simplemente muestra la versión de la librería.

¿Y como hacemos para leer el manifest del mismo fichero del cual nos estamos ejecutando? Pues averiguando donde se encuentra el .jar en tiempo de ejecución y accediendo a él para leer el fichero manifest. Podemos hacerlo así (código simplificado sin gestión de errores):

String jarFileURL = 
  MainApp.class.getProtectionDomain().getCodeSource().getLocation().toString();
int pos = jarFileURL.indexOf("!");
if(pos!=-1)
{
  jarFileURL = jarFileURL.substring(0,pos);
}
if(!jarFileURL.startsWith("jar:"))
{
  jarFileURL = "jar:" + jarFileURL;
}
URL manifestUrl = new URL(jarFileURL + "!/META-INF/MANIFEST.MF");
Manifest manifest = new Manifest(manifestUrl.openStream());      
return manifest.getMainAttributes().getValue(“MyLibrary-version”);

Y así podemos hacer que al ejecutar java -jar milibreria.jar nos devuelva la versión de la librería, o si lo ponemos en un método público, podemos usar este número para mostrarlo en las aplicaciones que usen la librería y así puedan saber que versión están utilizando, comprobar si existen nuevas versiones etc.

Espero que os sirva, al menos a mi me ha servido, y que vuestros usuarios estén más contentos :).

Happy coding! EJ

lunes, 30 de mayo de 2011

¿En qué se parece un informático a un limón?

En vez de hacer un chiste fácil, y seguramente malísimo, con la frase, voy a tratar de aportar mi grano de arena a la eterna discusión de "¿Por qué el mercado de informáticos está tan mal/desprestigiado y generalmente se cobra tan mal en relación a la valía?"

Para explicarme debo mencionar la teoría económica de un economista de 1970, George Akerlof, expuesta en un artículo bajo el título "The Market for Lemons: Quality Uncertainty and the Market Mechanism". En realidad en este caso "lemon" no se traduciría por limón, de ahí el chiste malo, si no por su acepción menos conocida de "cacharro" o "cosa decepcionante".
Por simplificar, la teoría viene a decir que cuando en un mercado de un artículo, el comprador no tiene información fiable a priori para saber si lo que compra es bueno o es malo, y el vendedor sí, al final en ese mercado se acaban vendiendo solamente los "cacharros", es decir, los artículos de peor calidad y a precios bajos.
Una explicación basada en un ejemplo la podéis encontrar en esta entrada del blog Fermat Margin.

Y eso es en parte lo que nos ocurre en el mercado de los informáticos, especialmente cuando tienen menos experiencia, ya que muchas veces quien contrata no sabe de que va el tema y no tienen experiencia previa, así que no pueden evaluar de forma fiable la valía de los candidatos.  Y la cosa va tal que así
  • El comprador (el que contrata) no tiene forma medianamente fiable de saber como le va a salir el artículo (el posible empleado). Para no pasarse, obviamente tira por un sueldo medianero.
  • Los buenos programadores que merecerían más sueldo, rechazan trabajar por ese sueldo y buscan otras cosas.
  • Por tanto entre los que aceptan esos trabajos, sólo quedan los que merecen ese sueldo y los que no.
  • Por tanto el comprador cuando mira lo que obtiene por lo que paga, se da cuenta de que la media le sale por debajo, así que la próxima vez ofrece sueldos más bajos.
  • Y así recursivamente, hasta que los sueldos son una mierda y en ese mercado sólo trabajan los malos empleados que no podrían aspirar a nada más. Por lo tanto los empleadores ven confirmada su idea de que realmente no ha de pagar más por que todos los que contrata son malísimos, y los buenos empleados no tienen ningún incentivo por hacerlo bien, puesto que les van a pagar como si fueran malos, así que al final se convierten en malos.
Y ahí tenemos un bonito circulo vicioso :). No lo explica todo, y por supuesto los buenos empleados pueden encontrar buenos empleos, si salen de ese mercado, y los empleadores pueden encontrar personal adecuado si no entran en este juego. Pero es una pieza más del puzzle, aparte de la avaricia, la cultura del pelotazo y todos esos tópicos que ya se mencionan suficiente. Simplemente me apetecía añadir este enfoque diferente y curioso que explica algunos de los comportamientos humanos que llevan a situaciones desastrosas.

Ni soy economista ni gurú, así que no tengo ni creo tener receta mágica para solucionar este tipo de problemas, pero si crees ser un buen empleado, no te conviertas en un limón y si eres un empleador, no vayas al mercado a por limones. Hay que recordar que la clave de este problema concreto es la información, o falta de ella, de una de las partes. Decir que con más información se soluciona es fácil, aplicarlo en la vida real es lo complicado ;).


Happy coding! EJ

sábado, 1 de enero de 2011

Estándar de nomenclatura en BDD

Hoy una entrada sobre el sistema personal de nomenclatura de BDD que me gusta utilizar en mis aplicaciones cuando tengo la capacidad de decidir. No siempre es así, a veces se trabaja con BDD heredadas o uno no es el responsable de esa área, pero cuando es posible, personalmente prefiero utilizar este sistema que me proporciona la sensación de tener más "ordenada" la BDD.

La idea, al fin y al cabo, es tener un sistema que sea fácil de seguir y que permita identificar fácilmente los elementos que se estan utiizando en consultas, los que se referencia en mensajes de error, en relaciones entre elementos etc.

Resaltar especialmente que este es el sistema que YO utilizo, pero hay muchos otros cada uno con sus pros y sus contras y no es mi intención decir que es el mejor ni nada remotamente parecido. En este caso, se cumple el dicho de que para gustos, colores y la organización o el DBA son los responsables de escoger entre la cantidad de opciones disponibles.

.- El primer punto es sencillo, escoger tres letras como alias de "la aplicación" o módulo a desarrollar. Por ejemplo, si vamos a trabajar la parte de almacén y no se ha usado ya ese "álias", podríamos escoger ALM como álias de aplicación.
.- Una vez escogido el primer punto, las tablas empezarían con T + dicho álias, las vistas con V + álias, las restricciones de integridad con K + álias, los índices con I + álias... etc. Así pues, la tabla para representar los productos en la aplicación sería TALM_PRODUCTO, la de clientes TALM_CLIENTE y así sucesivamente. Los nombres siempre en singular y sin abreviar a no ser que la longitud sea muy muy larga. Hoy en día restringir los nombres a 8 caracteres o menos como se hacía antes tiene muy poco sentido.
.- Dentro de las tablas, yo prefiero utilizar siempre un identificador único e inventado sin significado de negocio, siempre con el mismo nombre y el mismo tipo. Además, cada tabla tiene su propio álias y se añade delante de todos los nombres de los campos. Por ejemplo, la tabla TALM_CLIENTE, con alias CLI, tendria los campos CLI_ID, CLI_NOMBRE, CLI_NIF..., la de productos tendria PRD_ID, PRD_NOMBRE, PRD_CODIGO... Darse cuenta de que PROD_ID sería un identificador inventado y que el número que viene en el producto sería por ejemplo PRD_CODIGO. Existen múltiples discusiones a favor y en contra de usar o no identificadores inventados y no voy a intentar convencer a nadie. Sólo decir que tener que detener la operación del sistema periódicamente para poder anular las restricciones de integridad para solucionar los problemas de errores en los identificadores no inventados da una perspectiva diferente :). No voy a decir que no tenga inconvenientes o que el sistema esté mal hecho si no se sigue. Sólo puedo decir que cuando puedo elegir, YO lo hago así. Siguiendo con lo ya mencionado, la restriccion de clave primaría tiene el alias de la aplicacion y de la tabla seguido del sufijo _PK, para indicar que es "primary key". Por ejemplo, para la tabla TALM_CLIENTE sería KALM_CLI_PK, para TALM_PRODUCTO sería KALM_PRD_PK etc.
.- Las claves extranjeras simplemente referencian el campo en la tabla "remota" añadiendo el prefijo correspondiente y sólo en caso de existir más de una clave extranjera contra la misma tabla se añade un sufijo para diferenciarlas. Por ejemplo, en la tabla TALM_STOCK, tendríamos una clave extranjera a TALM_PRODUCTO bajo el nombre STK_PRD_ID, en TALM_FACTURA tendríamos FAC_CLI_ID para referenciar el cliente... etc. Como se puede ver esto facilita enormente la identificación de los elementos de cada relacion, incluso permite automatizar algunas tareas. Por otro lado, las restricciones de integridad de las claves extranjeras indican en este caso ambas tablas y el sufijo _FK, de "foreign ey". Las restricciones para los ejemplos mencionados serían KALM_STK_PRD_FK y KALM_FAC_CLI_FK. De este modo, al violar una de estas claves, el mensaje de error ya nos indicará claramente cuales con las tablas implicadas.
.- Las restricciones sobre campos indican el nombre del campo y el tipo de restriccion, por ejemplo PRD_CODIGO sería un campo seguramente único y como tal tendría la restriccion KALM_PRD_CODIGO_UK (de "unique key"), las restricciones de integridad serían igual pero con _CHK al final etc.

Ese es básicamente el estilo de nomenclatura que sigo y con el que me siento comodo, aunque no soy muy talibán al respecto.

A la hora de trasladar el modelo de datos a clases Java los prefijos desaparecen y uso un estilo de nomenclatura más "javero" y así de esta forma cada mundo es coherente y sigue sus propias reglas. Eso implica que siempre tengo que especificar los nombres de los elementos que corresponden en cada caso, pero es una "molestia" que tengo automatizada y no me importa pagar ese precio. La razoón para no tener que usar tanto sufijo en Java es que con la estructuración en paquetes y el estilo de los errores ya da suficiente información como para poder situarte rápidamente sin necesitar esas ayudas.

No intento convencer a nadie de que use nada pero por si a alguien le da alguna idea y/o le sirve, ahí queda dicho. Y una vez dicho esto...¿Usais vosotros, estimados lectores, algún tipo de estilo/estándar de nomenclatura?

Happy coding! EJ

PD: Ah, feliz año nuevo ;). A ver si este 2011 nos trata un poco mejor a "la plebe" y un poco peor a los chupasangres del mundo.