API del buscador de euskadi.eus

El acceso a cualquier funcionalidad de la infraestructura de euskadi.eus est encapsulado en un API Java que permite:

  • Gestionar la taxonoma de etiquetas de catalogacin comn de Euskadi.eus.
  • Trabajar con contenidos y versiones idiomticas: crear, borrar, modificar, aprobar, catalogar, etc.
  • Trabajar con portales o sitios web: sus pginas y reas visuales.
  • Publicar contenidos, pginas de portal, etc.
  • Indexar en uno o varios motores de bsqueda cualquier contenido o pgina de portal.
  • Buscar contenidos o pginas de portal.

Los diferentes APIs (contenidos, portal, publicador, buscador, etiquetas de catalogacin, etc.) abstraen al programador de la lgica de negocio implementada en los ncleos de la infraestructura que es donde realmente reside la inteligencia del sistema.

De todos los APIs disponibles, el API del Buscador es uno de los ms interesantes de cara a la reutilizacin ya que facilita la localizacin de contenidos y sus meta-datos para su procesado; este API tiene dos sabores:

API pseudo-REST

El API pseudo-REST permite crear consultas de bsqueda codificadas en una URL del navegador que se lanzan contra el ncleo de bsqueda de euskadi.eus y devuelve el resultado en XML.

API Java

El API Java facilita la creacin de queries y el tratamiento de los resultados devueltos frente al API pseudo-REST.

1.Crear la Query

R01MSearchQuery qry = R01MSearchQuery.create()
    .typedInAnyOfTheeseClusters(R01MTypoClusterOID.forId("euskadi"))     
    .typedInAnyOfTheeseFamilies(R01MTypoFamilyOID.forId("procedimientos_administrativos"))
    .typedInAnyOfTheeseTypes(R01MTypoTypeOID.forId("ayuda_subvencion"))
    .mustMeetTheeseMetaDataConditions(R01MSearchQueryNumberMetaData
                                           .forMetaData("procedureStatus")
                                           .usingCondition(R01MSearchQueryNumberMetaDataCondition.EQUALS)
                                           .with(16))
    .mustHaveStructureLabel("label1");

2.Crear la sesin de bsqueda

Es preciso crear una sesin de bsqueda para conseguir los resultados del servicio: cuntos resultados, paginacin, nmero de elementos:

R01MSearchSession session = R01MSearchSession.forQuery(qry);

R01MSearchResultItem[] items = session.getCurrentPageSearchResults()

StringBuffer dbg = new StringBuffer();

    if (items != null) {

           for (R01MSearchResultItem item : items) {

            dbg.append("-").append(item.getContentName())

                 .append(": ").append(item.getDocumentName()).append("\r\n");

           }

    }

Ejemplo completo

Los pasos a seguir son los siguientes:

  1. Componer la query para filtrar los contenidos deseados
  2. Obtener una sesin de bsqueda (ejecutar la query)
  3. Iterar sobre los tems de resultados
  4. Obtener meta-datos devueltos por el buscador:
    • Meta-datos comunes como el nombre del contenido, la versin idiomtica, fechas de publicacin, etc
    • Meta-datos especficos del tipo de contenido indexados en el motor de bsqueda

Ejemplo: de una ayuda se indexan algunos meta-datos buscables como el estado de vigencia; estos metadatos son devueltos en los resultados de bsqueda. Dado que el buscador NO tiene todos los meta-datos del tipo de contenido es necesario acceder al XML que contiene los meta-datos especficos de cada tipo de contenido.

NOTA: En los documentos publicados en opendata.euskadi.eus se puede tener una visin de la estructura de los contenidos de Euskadi.eus:

Para acceder al XML de los meta-datos especficos dependientes del tipo de contenido hay dos opciones:

  1. Acceder directamente al XML que contiene los metadatos va HTTP
  2. Descargar un fichero ZIP con el contenido y recuperar el XML del ZIP

En cualquiera de los dos casos es importante conocer la estructura de directorios de los contenidos de Euskadi.eus; que en grandes lneas es el siguiente:

[contenido]

|- [contentName]-content.xml
|- [contentName]-idxContent.xml
|- [lang]_[version]
|_[data]

|- [dataFile] <<< Este fichero es el que interesa ya que contiene los meta-datos especficos del tipo de contenido

Por lo tanto, para cada resultado habr que:

  1. Obtener la carpeta que contiene los datos de la versin idiomtica
  2. Obtener el nombre del fichero que contiene los datos especficos
  3. Descargarse el fichero con los datos especficos
  4. Parsear el ficheoro XML para acceder a los datos

Un ejemplo de la secuencia de pasos anterior es la siguiente:

/**
* Construir una query para buscar las ayudas de Euskadi.eus
* @param items los items a procesar
* @return
*/
private static R01MSearchQuery _buildQuery() {public static void main(String[] args) {
   try {
       // Construir la query
       R01MSearchQuery qry = _buildQuery();
       
       // Inicializar la sesioacute;n
       R01MSearchSession session = R01MSearchSession.forQuery(qry);
       
       System.out.println(session.queryAsURL());
       
       // Un poco de debug
       String dbg = Strings.create()
                           .add("[Resultados de la bsqueda: \r\n")
                           .add("\t- query como XML: ").addLine(session.queryAsXML())
                           .add("\t- query codificada en la URL: ").addLine(session.queryAsURL())
                           .add("\t- Resumen de la ejecución{} resultados en {} páginas de {} items (max) cada una")
                                   .customizeWith(session.getPager().getTotalNumberOfItems(),
                                                  session.getPager().getPageCount(),
                                                  session.getPager().getPageSize())
                           .asString();
       System.out.println(dbg);
       
       // Recoger resultados
       R01MSearchResultItem[] resultItems = session.getCurrentPageSearchResults();
       System.out.println(_processResultItems(resultItems));
       
       // Ir a la siguiente p´gina de resultados
       resultItems = session.goToNextPage();
       System.out.println(_processResultItems(resultItems));
       
       // ... mas...
       
    } catch (Exception ex) {
       ex.printStackTrace(System.out);
    }
}

    R01MSearchQuery qry = R01MSearchQuery.create()
                                    .typedInAnyOfTheeseClusters(R01MTypoClusterOID.forId("euskadi"))
                                    .typedInAnyOfTheeseFamilies(R01MTypoFamilyOID.forId("procedimientos_administrativos"))
                                    .typedInAnyOfTheeseTypes(R01MTypoTypeOID.forId("ayuda_subvencion"))
                                    .mustMeetTheeseMetaDataConditions(R01MSearchQueryNumberMetaData
                                                                             .forMetaData("procedureStatus")
                                                                             .usingCondition(EQUALS)
                                                                             .with(16));

    return qry;
}

/**
* Procesar los resultados de bsqueda: acceder a algunos datos:
*      - MetaDatos comunes como el nombre del contenido y versión idiomáca
*      - MetaDatos específicos: XML del tipo de contenido
* @param items los items a procesar
* @return
*/
private static String _processResultItems(final R01MSearchResultItem[] items) {
    StringBuffer dbg = new StringBuffer();

    if (items != null) {

       for (R01MSearchResultItem item : items) {
           ItemData itemData = new ItemData();
       
           // Algunos metaDatos comunes
           // -------------------------
           itemData.setContentName(item.getContentName());
           itemData.setLang(item.getDocumentLanguage());
           itemData.setLangVersionName(item.getDocumentName());
           
            // Algunos meta-datos relevantes dependientes del tipo de contenido
           // pero que son devueltos por el buscador
           // ----------------------------------------------------------------
           Map indexedMD = item.getDocumentMetaData();
           itemData.setProcedureStatus(indexedMD != null ? indexedMD.get("procedureStatus").toString() : null);
           

           // MetaDatos específicos disponibles en un XML que hay que descargar de Euskadi.eus
           // --------------------------------------------------------------------------------
           // Buscar el datafile (esta parte NO estaba pensada para ser utilizad en OpenData, pero
           // es prácticamente la única opción que hay WTF!)
           // item.getDocumentDataFilesGeneratedFilesDocumentRelativePaths() es un Mapa
           // que relaciona los oids de los datafiles (que es lo que se necesita) con el fichero HTML
           // generado
           // Ej:  
           //          procedimiento_ayuda_v2;main:abandono_2010.html 
           //          
           //      
           // (se necesita el oid: r01dpd013a780b8c401e41497a285409dc514c34e)
           Map dataFiles = item.getDocumentDataFilesGeneratedFilesDocumentRelativePaths();
           String dataFileOid = !CollectionUtils.isNullOrEmpty(dataFiles) ?
                                         CollectionUtils.of(dataFiles.keySet()).pickOneElement()
                                           :
                                         null;
           
           String dataFileXML = null;
           if (dataFileOid != null) {

               try {

                   String dataFileURL = "http://www.euskadi.eus/contenidos/" +

                                              item.getDocumentWorkAreaRelativePath() +

                                              "/data/" + item.getDocumentLanguage() + "_" + dataFileOid;

                   dataFileXML = HttpClient.forUrl(dataFileURL)

                                           .withoutParameters()

                                           .usingGETRequestMethod()

                                           .loadAsString();

                  dataFileXML = dataFileXML.trim();

               } catch (Exception ex) {

                   ex.printStackTrace(System.out);

               }

           }
           itemData.setDataFileXML(dataFileXML);
           
           // Assets reusables (ZIP con el contenido)
           // ---------------------------------------
           Map rispAssets = item.getContentRispDocumentsInfo();
           if (!CollectionUtils.isNullOrEmpty(rispAssets)) {
               Map assets = new HashMap(rispAssets.size());
               for (Map.Entry assetInfo : rispAssets.entrySet()) {
                   Path assetPath = Path.of(assetInfo.getValue().split(",")[0]);
                   assets.put(assetInfo.getKey(),assetPath);
               }
               itemData.setReusableAssets(assets);
           }
           // Debug:
           System.out.println("\r\n\r\n\r\n_________________________________________________\r\n" +
                              itemData.debugInfo());
       }
    }
    return dbg.toString();
}

@Accessors(prefix="_")
static class ItemData implements Debuggable {
    @Getter @Setter private String _contentName;
    @Getter @Setter private String _lang;
    @Getter @Setter private String _langVersionName;
    @Getter @Setter private String _procedureStatus;
    @Getter @Setter private Map _reusableAssets;
    @Getter @Setter private String _dataFileXML;
    
    @Override
    public String debugInfo() {
       return Strings.create().add("     Content Name: ").addLine(_contentName)
                              .add("Lang Version Name: ").add("(").add(_lang).add(")").addLine(_langVersionName)
                              .add(" Procedure status: ").addLine(_procedureStatus)
                              .add("  Reusable Assets: ").addLine((_reusableAssets != null ? 
                                                                                _reusableAssets.values().toString()
                                                                                :
                                                                                ""))
                              .add("     DataFile XML: ").add(_dataFileXML)
                              .asString();
    }
}

Como se puede ver la obtencin de datos de opendata.euskadi.eus es relativamente sencilla, aunque antiene cierta complejidad ya que el programador ha de:

  • Ser consciente de la estructura de directorios de euskadi.eus y dnde se encuentran los meta-datos especficos del tipo de contenido (tipos de contenido y metadatos de euskadi.eus).
  • Parsear el fichero XML de meta-datos especficos