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 taxonomía de etiquetas de catalogación común de Euskadi.eus.
- Trabajar con contenidos y versiones idiomáticas: crear, borrar, modificar, aprobar, catalogar, etc.
- Trabajar con portales o sitios web: sus páginas y áreas visuales.
- Publicar contenidos, páginas de portal, etc.
- Indexar en uno o varios motores de búsqueda cualquier contenido o página de portal.
- Buscar contenidos o páginas de portal.
Los diferentes APIs (contenidos, portal, publicador, buscador, etiquetas de catalogación, etc.) abstraen al programador de la lógica de negocio implementada en los núcleos 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 más interesantes de cara a la reutilización ya que facilita la localización 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 búsqueda codificadas en una URL del navegador que se lanzan contra el núcleo de búsqueda de euskadi.eus y devuelve el resultado en XML.
- Manual técnico del buscador de euskadi.eus (pdf, 750 Kb)
- Ejemplo de utilización del API pseudo-REST: reutilizar las ayudas y subvenciones de Euskadi.eus
- Familias y tipos de contenido de euskadi.eus y sus metadatos
API Java
El API Java facilita la creación de queries y el tratamiento de los resultados devueltos frente al API pseudo-REST.
- Documentación para utilizar el API Java / Instalación de las librerías en eclipse (pdf, 750 Kb)
- Librerías necesarias para utilizar el API java (Zip, 11 Mb)
- Familias y tipos de contenido de euskadi.eus y sus metadatos
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 sesión de búsqueda
Es preciso crear una sesión de búsqueda para conseguir los resultados del servicio: cuántos resultados, paginación, número 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:
- Componer la query para filtrar los contenidos deseados
- Obtener una sesión de búsqueda (ejecutar la query)
- Iterar sobre los ítems de resultados
- Obtener meta-datos devueltos por el buscador:
-
- Meta-datos comunes como el nombre del contenido, la versión idiomática, fechas de publicación, etc
- Meta-datos específicos del tipo de contenido indexados en el motor de búsqueda
Ejemplo: de una ayuda se indexan algunos meta-datos buscables como el estado de vigencia; estos metadatos son devueltos en los resultados de búsqueda. Dado que el buscador NO tiene todos los meta-datos del tipo de contenido es necesario acceder al XML que contiene los meta-datos específicos de cada tipo de contenido.
NOTA: En los documentos publicados en opendata.euskadi.eus se puede tener una visión de la estructura de los contenidos de Euskadi.eus:
Para acceder al XML de los meta-datos específicos dependientes del tipo de contenido hay dos opciones:
- Acceder directamente al XML que contiene los metadatos vía HTTP
- 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 líneas 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 específicos del tipo de contenido
Por lo tanto, para cada resultado habrá que:
- Obtener la carpeta que contiene los datos de la versión idiomática
- Obtener el nombre del fichero que contiene los datos específicos
- Descargarse el fichero con los datos específicos
- 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 obtención de datos de opendata.euskadi.eus es relativamente sencilla, aunque aún tiene cierta complejidad ya que el programador ha de:
- Ser consciente de la estructura de directorios de euskadi.eus y dónde se encuentran los meta-datos específicos del tipo de contenido (tipos de contenido y metadatos de euskadi.eus).
- Parsear el fichero XML de meta-datos específicos