Programación

Publicaciones que no se centran en ningún lenguaje de programación en concreto

¡Java y XML se entienden!

Hace un par de semanas tuve que ponerme de nuevo manos a la obra en algo que había tocado poco o prácticamente nada. El mayo de mis problemas es que llevaba casi dos años sin tocar el lenguaje Java y aunque PHP se le parece no deja de ser solamente eso, que se le parece… pero ni de lejos llega a ser completamente el mismo lenguaje.

La historia es la siguiente. Desde hace ya bastante tiempo veníamos usando un programita que nos hicieron en su día en VisualBasic, que para colmo de males no tenemos ni el código fuente, que cogía los teletipos que provenían de una agencia de noticias en formato XML y los traspasaba, previamente habiendo sido analizados, a una base de datos. El problema vino cuando dichos teletipos cambiaron internamente el formato. El programa no los reconocía. Ni corto ni perezoso abrí mi queridísimo Eclipse, instalé nuevamente el compilador de Java y ¡¡a trabajar!!

La primera decisión, ¿qué librería usaremos para leer los XMLs? En fin, tiré de biblioteca universitaria desempolvando mis viejos apuntes de carrera de cuando programar en Java era algo que tenía que hacer todos los días y… entonces recordé aquellos “maravillosos” momentos en los que SAXBuilder me tuvo un día hasta cerca de las 3:30 de la madrugada para poder hacer una práctica. Como se suele decir, más vale malo conocido que bueno por conocer, así que está claro que si en su día funcionó, ¿por qué no lo va a hacer otra vez?

Tras unas cuantas horas comiéndome el coco la verdad es que la historia quedó bastante elegante. Os dejo aquí un par de funciones que he implementado, ya que incorporar a esta entrada del blog el programa entero sería un absurdo y lo interesante es que se vea lo sencillo que es recorrer un XML en Java una vez que se conoce la estructura interna del mismo.

Comencemos con el programita…

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

Para leer un archivo XML dado se debe de hacer de la siguiente manera. Primeramente se crea un objeto de clase SAXBuilder que será donde cargaremos el XML. A continuación leemos el archivo desde el disco utilizando el objeto creado y pasándole como parámetro la ruta del archivo que deseamos analizar. Finalmente se crea un objeto de clase Element (¡sí! ¡Element! ¡Viva lo genérico!) y mediante el método getRootElement tendremos ya lista la raíz del archivo XML para empezar a trabajar.

SAXBuilder builder = new SAXBuilder();
Document doc=builder.build(“c:midirectorioarchivo.xml”);
Element e = doc.getRootElement();

¿Y ahora qué hacemos con todo esto?

Imaginemos que nuestro xml internamente tuviera algo así..

<raíz>

<identificador>12345</identificador>

….

</raíz>

… y quisiéramos leer lo que hay en identificador. Una forma un tanto “animal” sería la siguiente:

encontrado = false;

List<Element> elementos = e.getChildren();
Iterator i = elementos.iterator();
while ((i.hasNext()) && (!encontrado)) {
Element e2 = (Element) i.next();
String texto = e2.getName();
if (texto == “identificador”) {
String identificador = e2.getText();
encontrado = true;
}

y en nuestra variable identificador tendriamos el texto que hay en la etiqueta <identificador> del XML.

Otra forma un tanto más elegante de hacerlo, y que conste que el código se podría mejorar muchísimo más pero que conste que es un simple ejemplo, sería la siguiente…

public static Element getSubNodo(Element e, String subnodo) {
List<Element> elementos = e.getChildren();
Iterator i = elementos.iterator();
while (i.hasNext()) {
Element e2 = (Element) i.next();
String texto = e2.getName();
if (texto == subnodo)
return e2;
}
return e;
}

public static String buscarIdentificador(Element e) {
        String devuelve = “”;
        Element nodo = getSubNodo(e, “identificador”);
        devuelve = nodo.getText();
        return devuelve;
    }

Y la llamada sería:

String identificador = buscarIdentificador(e);

Pues con estas dos bonitas funciones y un par más de mi cosecha que buscaban aún más “hondamente” dentro del XML se pudo leer sin problemas el XML en cuestión, quitando de un plumazo un programa hecho en el horrible, vetusto y anticuado VisualBasic por algo más decente y moderno como es una aplicación hecha en Java.

Los primeros pasos con Lucene bajo PHP. Al final funcionará y todo

Ayer, y tal como ponía en mi anterior publicación me empeñé en echar a andar el motor de búsqueda Lucene sobre PHP. Mi intención era clara, tengo una base de datos en MySQL un poco “toqueteada internamente por mí” para que funcione mejor a nivel de consultas Full-Text que no me termina de convencer, por lo tanto manos a la obra…

Tras “googlear” un poco en busca de información (malos hábitos que creo que ya tiene el 99.9% de los mortales que se dedican a esta nombre y malpagada profesión) encuentro varios documentos en “cristiano” sobre cómo implementar en Ubuntu (sistema que uso como servidor en la empresa) el motor Lucene bajo PHP. La primera sorpresa es que Lucene en PHP tira de Zend, pero tampoco me preocupa demasiado, a pesar de que no soy muy amigo de utilizar Frameworks. En fin, que lo peor de todo es que la información que encuentro es ANTIGUA DE NARICES, muy muy obsoleta, pero por suerte hay una página que prácticamente explica todo lo que me hace falta: https://www.phpriot.com/articles/zend-search-lucene.

Vamos allá… instalamos Zend:

$ sudo apt-get install libzend-framework-php

Y parece que todo va bien.. Ubuntu no se queja así que vamos a hacer nuestro primer código fuente con los ejemplos modificados que hay en la página que hemos mencionado anteriormente..

$path = “/mi_path/a_un_lugar/para_el_indice”;

if(!is_dir($path)){
    echo “Creando Path…<br>”;
       $index = Zend_Search_Lucene::create($path);
}
else{
   $index = Zend_Search_Lucene::open($path);
}

y primera castaña… el script arroja un error de que no reconoce la clase Zend_Search_Lucene….. Perooooo… ¿no la acabo de instalar? Mmmmm… a lo mejor hay que reiniciar Apache.. Procedemos..

$ sudo /etc/init.d/apache restart

Probamos otra vez… ¡¡PEEEEEKK!! Error…. ¡¡arrrghh!! En fin, revisemos nuevamente la documentación a ver qué se me está olvidando…..

5 minutos más tarde

Las prisas, o la agonía, vete tú a saber. Si no se le dice al script que use la librería Zend, ¿cómo va a funcionar? Solución, al principio del script se pone:

require_once(“Zend/Search/Lucene.php”);

Pero como se ve que ayer tuve muy mala suerte me vuelve a dar unos bonitos mensajes de error diciendo que no se puede insertar la librería… Vuelta a “San Google”…. y nada… así que me armo de valor y empiezo a mirar qué archivos ha modificado el sistema y veo que Zend crea un archivo de configuración dentro del arbol de PHP llamado  /etc/php5/conf.d/zend-framework.ini. Lo edito y veo que el path donde va a buscar los archivos está ¡¡¡COMENTADO CON UN PUNTO Y COMA!!! Perooooo… en fin, quito el dichoso “punto y coma”, recargo la configuración de Apache por si las moscas y… ¡¡ahora sí!! Ni un error y en el path que he definido del sistema se ha creado una bonita carpeta con varios archivos dentro que según leo en la documentación de Lucene son los índices. Primer paso dado, vamos a darle caña a hacer algo añadiendo las siguientes líneas a nuestro script:

    $doc = new Zend_Search_Lucene_Document();
    $doc->addField(Zend_Search_Lucene_Field::UnIndexed(‘dia’, ‘2011-12-29’));
    $doc->addField(Zend_Search_Lucene_Field::UnIndexed(‘pagina’, ‘3’));
    $doc->addField(Zend_Search_Lucene_Field::UnStored(‘texto’, ‘Manolito es una persona que ha hecho muchas cosas’));
    $index->addDocument($doc);

Ejecutamos y…parece que todo ha ido bien…. ¿Funcionará la búsqueda?

Realizamos el siguiente script y lo ejecutamos:

require_once(“Zend/Search/Lucene.php”);

$path = “/mi_path/a_un_lugar/para_el_indice”;
Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding(‘utf-8’);
Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive());
if(!is_dir($path)){
    die(“El indice no existe”);
}
else{
   $index = Zend_Search_Lucene::open($path);
}

$patron = “manolito”;

echo “Buscando “$patron”…<br>”;
echo “Documentos Totales:”.$index->numDocs().”<br>”;
$hits  = $index->find($patron);
$numHits = count($hits);
echo “Encontradas $numHits coincidencias<br>”;
foreach($hits as $hit){
    echo $hit->dia.”<br>”;
    echo $hit->pagina.”<br>”;    
    echo $hit->score.”<br>”;
}

¡¡¡¡Oooohhhhh!!!!! ¡¡Alegría por todo mi ser!! Me dice que ha encontrado una coincidencia (estoy buscando la palabra “manolito) y que el número total de documentos almacenados es 1.

Conclusión, PHP también parece que se entiende con Lucene, y para los que nos tenemos que pelear con buscadores de palabras parece que se nos abre el cielo. Próximamente intentaré implementarlo de verdad con unas cuantas cosas que tengo en mente, pero eso será “más adelante”…

Buscar o no buscar, el caso es que sea rápido

Hace unos cuantos meses terminé de desarrollar un proyecto de Hemeroteca digital para el medio de publicación para el que trabajo que sinceramente, me dejó más que satisfecho porque por una vez en la vida he podido programar a mis anchas, sin grandes presiones de tiempo. Muchas veces esas dichosas presiones hacen que uno empiece a volverse loco y tu código fuente, sea cual sea el lenguaje que en el que uno esté programando, se convierta más en un cutreprograma tipo Basic de los que solíamos hacer al principio de los tiempos cuando nadie nos había explicado lo que era la programación estructurada. Este proyecto unificaba lo mejor de mis conocimientos en PHP5 y amplió hasta nuevos horizontes cosas muy peculiares que he descubierto que se pueden hacer en JavaScript y por supuesto añadir una dimensión desconocida a JQuery, una maravilla que cada día me sorprende más.

Y sin embargo todo tiene un “pero”… El sistema de búsquedas basado en MySQL parecía ir a las mil maravillas cuando en las primeras pruebas solo teníamos 5 o 6 años de publicaciones hechas, pero cuando se hizo la indexación de todo nuestro fondo editorial (65 años) la cosa ya no era lo que parecía. Descubrí que las búsquedas Full-Text de MySQL no eran nada del otro mundo, de hecho funcionan muy pero que muy bien usando una o dos palabras, pero como se nos ocurra hacer una búsqueda “tipo Google”.. en fin… puede ser que tengamos suerte y tarde 3 segundos la búsqueda o que tarde perfectamente un minuto. ¿Cómo es posible? Mi gozo en un pozo… pero como hay remedio para todo, o eso espero hoy me he propuesto intentar hacer pruebas con Lucene.

Lucene es un motor realizado por Apache Group y que como todo lo que sale de esta gente es lo más de lo más. El primer problema con el que me he encontrado es que está originalmente hecho para Java, ¡pero tranquilidad! ¡Hay versiones ya para todo! Ruby, PHP, Python, etc…

Probemos pues y veamos en qué acaba el asunto….