Apache Solr
Dec 06, 2014
Apache Solr
Expertos en sistemas de búsqueda, repositorios digitales y recomendación.
Referencia Relevante: 24Symbols, BBVA o Biblioteca Nacional.
¿Quiénes Somos?
www.openandsearch.com
Índice
• Conceptos y descripción de Apache Solr
• Preparando el terrenoo ¿Cómo lo instalamos?o Configurando Apache Solr
• Con las manos en la masao Modificando el índiceo Consultando el índiceo Análisis personalizado
• Clustering
• Operaciones y Mantenimiento
¿Qué es Apache Solr?
• Es un servidor de búsqueda full-text basado en Apache Solr
• Utiliza protocolos estándar de comunicación como HTTP y HTTPS
• Es altamente escalable
• Es totalmente modular gracias a su sistema de plugins
• Ofrece una interfaz (simple) de administración web
• Está construido utilizando la tecnología de Apache Lucene
Componentes Principales
• RequestHandlers: Procesan la consulta solicitada
• SearchComponents: Añaden datos (de la consulta) a la respuesta
• ResponseWriters: Transforman a formato de respuesta concreto.
• SolrCore: conjunto de configuración y documentos
• UpdateHandler: Lógica de actualización de documentos
• Cache: Estrategia de caching.
Instalando Apache Solr: Requisitos
• Java 1.5 o posterior (http://www.java.com/es/download)
• Servidor de Aplicaciones con soporte para Servlets 2.4
• Tomcat 5.5.X+
Instalando Apache Solr: Despliegue
• Despliegue del fichero WAR en contenedor
• Configuración del SOLR_HOME• JNDI• Variable de entorno
• Estructura del directorio SOLR_HOMEo solrconfig.xmlo schema.xmlo solr.xml (opcional)
Configurando Apache Solr: Ficheros
• solr.xml: configuración de los SolrCores activos
• schema.xml: Configuración de campos y procesado
• solrconfig.xml: Configuración de componentes
• Varios: Ficheros de sinónimos, stopwords, etc.
Configurando Apache Solr: solr.xml
<solr persistent="true" sharedLib="lib"> <cores adminPath="/admin/cores">
<core name="pdg" instanceDir="/etc/solr/cores/pdg" /> <core name="geoeuskadi" instanceDir="/etc/solr/geoeuskadi" /> <core name="24symbols" instanceDir="/etc/solr/24symbols" /> <core name="cmt" instanceDir="/etc/cores/cmt" /> <core name="disofic" instanceDir="/etc/solr/disofic" /> <core name="cdl" instanceDir="/etc/solr/cdl" /> <core name="24sac" instanceDir="/etc/solr/24symbolsac" />
</cores></solr>
Configurando Apache Solr: schema.xml<schema name="opensearch" version="1.1"> <types> <fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/> <fieldType name="text" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> <tokenizer class="solr.HTMLStripStandardTokenizerFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.ISOLatin1AccentFilterFactory"/> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/> </analyzer> <analyzer type="query"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.ISOLatin1AccentFilterFactory"/> <filter class="solr.SynonymFilterFactory" ignoreCase="true" synonyms="synonyms.txt"/> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/> </analyzer> </fieldType> </types> <fields> <field name="id" type="string" stored="true" indexed="true"/> <field name="docName" type="text" stored="true" indexed="true"/> <dynamicField name="fecha*" type="date" indexed="true" stored="true"/> <field name="buscable" type="text" stored="false" indexed="true" multiValued="true" positionIncrementGap="50"/> </fields> <copyField source="docName" dest="buscable"/> <uniqueKey>id</uniqueKey> <defaultSearchField>buscable</defaultSearchField> <solrQueryParser defaultOperator="AND"/></schema>
• fieldType: Definen el campo y el comportamiento a la hora de indexarlo
<fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
• field: Instancia de un tipo de campo bajo una clave propia
<field name="id" type="string" stored="true" indexed="true"/>
• dynamicField: Conjunto de campos bajo el mismo tipo y el mismo patrón
<dynamicField name="fecha*" type="date" indexed="true" stored="true"/>
• copyField: Copia, antes del procesado, el valor de un campo a otro
<copyField source="docName" dest="buscable"/>
• uniqueKey: Campo que se utilizará como identificador principal
<uniqueKey>id</uniqueKey>
• defaultSearchField: Campo de búsqueda por defecto
<defaultSearchField>buscable</defaultSearchField>
• solrQueryParser: Indica cómo se agruparán las clausulas de la consulta
<solrQueryParser defaultOperator="AND"/>
Configurando Apache Solr: schema.xml
• Analyzers: Se componen de un tokenizer y un conjunto de filtros <analyzer type="index"> <analyzer type="query">
• Tokenizer: Dividen el texto en un conjunto de tokens <tokenizer class="solr.StandardTokenizerFactory"/>
• Filter: Procesan cada uno de los tokens por separado <filter class="solr.ISOLatin1AccentFilterFactory"/>
<filter class="solr.SynonymFilterFactory" ignoreCase="true" synonyms="synonyms.txt"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
Configurando Apache Solr: schema.xml
Configurando Apache Solr: solrconfig.xml Propiedades del índice
<dataDir>/var/solr/data</dataDir><indexDefaults> <!-- Values here affect all index writers and act as a default unless overridden. → <useCompoundFile>false</useCompoundFile> <mergeFactor>10</mergeFactor> <!-- If both ramBufferSizeMB and maxBufferedDocs is set, then Lucene will flushbased on whichever limit is hit first. -->
<!--<maxBufferedDocs>1000</maxBufferedDocs>--><!-- Sets the amount of RAM that may be used by Lucene indexingfor buffering added documents and deletions before they areflushed to the Directory. --><ramBufferSizeMB>32</ramBufferSizeMB><!-- <maxMergeDocs>2147483647</maxMergeDocs> --><maxFieldLength>10000</maxFieldLength><writeLockTimeout>1000</writeLockTimeout><commitLockTimeout>10000</commitLockTimeout><lockType>native</lockType>
</indexDefaults><mainIndex>
<useCompoundFile>false</useCompoundFile><ramBufferSizeMB>32</ramBufferSizeMB>
<mergeFactor>10</mergeFactor></mainIndex>
Configurando Apache Solr: solrconfig.xml Configuración de consulta
<query> <maxBooleanClauses>1024</maxBooleanClauses> <filterCache class="solr.FastLRUCache" size="512" initialSize="512" autowarmCount="0" /> <queryResultCache class="solr.LRUCache" size="512" initialSize="512" autowarmCount="0" /> <documentCache class="solr.LRUCache" size="512" initialSize="512" autowarmCount="0" /> <enableLazyFieldLoading>true</enableLazyFieldLoading> <cache name="myUserCache" class="solr.LRUCache" size="4096" initialSize="1024" autowarmCount="1024" regenerator="org.mycompany.mypackage.MyRegenerator" />
<queryResultWindowSize>20</queryResultWindowSize> <queryResultMaxDocsCached>200</queryResultMaxDocsCached> <listener event="newSearcher" class="solr.QuerySenderListener"> <arr name="queries"> <!-- <lst> <str name="q">solr</str> <str name="rows">10</str> </lst> --> </arr> </listener> <useColdSearcher>false</useColdSearcher> <maxWarmingSearchers>2</maxWarmingSearchers></query>
Configurando Apache Solr: solrconfig.xml Configuración de los RequestHandlers
<requestHandler name="/spell" class="solr.SearchHandler" lazy="true"> <lst name="defaults"> <!-- omp = Only More Popular --> <str name="spellcheck.onlyMorePopular">false</str> <!-- exr = Extended Results --> <str name="spellcheck.extendedResults">false</str> <!-- The number of suggestions to return --> <str name="spellcheck.count">1</str> </lst> <arr name="last-components"> <str>spellcheck</str> </arr> </requestHandler> <searchComponent name="spellcheck" class="solr.SpellCheckComponent"> <str name="queryAnalyzerFieldType">textSpell</str>
<lst name="spellchecker"> <str name="name">default</str> <str name="field">name</str> <str name="spellcheckIndexDir">./spellchecker</str> </lst> </searchComponent>
Modificando el índice: indexación
• Formatos admitidoso XML: Formato de intercambio por defectoo Binario: Serialización Java (SolrJ) - commons-codeco Ficheros binarios: indexación de ficheros DOC, PDF,etc
utilizando Solr Cell (Apache Tika)
• Limitacioneso Visibilidad: no son visibles hasta que suceda el commit
(automático o manual)o Encoding: solo se soporta indexación en UTF-8o Actualización: No existe tal operación. Se añade un documento
bajo mismo id.
Modificando el índice: indexación Formato de XML de indexación
<?xml version="1.0" encoding="UTF-8"?> <add> <doc> <field name="identificador">http://url.cualquiera.com/webclient/</field> <field name="pid">1865905</field> <field name="thumbnail">http://url.cualquiera.com/webclient/Delivery</field> <field name="autor">Castro, Adolfo de 1823-1898 </field> <field name="autor_facet">Castro, Adolfo de</field> <field name="autor_abreviada">Castro, Adolfo de-1823-1898-</field> <field name="autor_completa">Castro, Adolfo de-1823-1898-#</field> <field name="titulo">Cádiz en la Guerra de la Independencia : cuadro histórico</field> <field name="editor">Cádiz Revista Médica </field> <field name="materia">Cádiz Historia S.XIX </field> … </doc> ... </add>
Modificando el índice: borrado• Tipos de borrado
o ID: Permite borrar una colección de documentos indicando sus identificadores
o Query: Permite especificar una consulta de Lucene. Todos aquellos que satisfagan la consulta serán borrados.
• Limitacioneso Visibilidad: no son visibles hasta que suceda el commit (automático o
manual)o Encoding: solo se soporta indexación en UTF-8
<?xml version="1.0" encoding="UTF-8"?><delete> <id>5c9e2d7e-7114-31cf-8582-56f11cefbcce</id> <id>d0b58756-8a8b-3de2-8327-55023cbdf16f</id> <id>4d9cb11a-2e2a-34e5-821d-5c7f77a41f8f</id> <id>fe722eac-d4cb-30f3-b2b5-b9e72630f523</id> ...</delete>
<?xml version="1.0" encoding="UTF-8"?><delete> <query>category: "Facturas"</query></delete>
Mediante consulta Mediante IDs
Modificando el índice: operaciones
• commit: Las indexaciones y borrados son ejecutados de forma definitiva
• rollback: Elimina las indexaciones y borrados pendientes de ejecución
• optimize: Reorganiza el índice y construye un solo segmento
• Consideraciones y limitacioneso Las transacciones no están asociadas a sesiones
o A mayor número de documentos en sesión, mayor consumo de memoria
o Bloqueo largo en commits
o Degradación paulatina del índice
• Indexación/borrado: Envío de XML a Solr
curl http://127.0.0.1:8983/solr/update -H "Content-Type: text/xml" -d @data.xml
• Commit sin nuevos documentos
curl http://127.0.0.1:8983/solr/update?commit=true
• Commit no bloqueante:
curl http://127.0.0.1:8983/solr/update?commit=true&waitSearcher=false
• Commit con nuevos documentos
curl http://127.0.0.1:8983/solr/update?commit=true -H "Content-Type: text/xml" -d @data.xml
• Optimize curl http://127.0.0.1:8983/solr/update?optimize=true
Modificando el índice: práctica
Consultando el índice: básicos
• El protocolo de comunicación es HTTP
• Los formatos de respuesta son variadoso XML o JSONo binario (commons-codec / java serialization)o do-it-yourself
• Aplica teoría de conjuntos mediante el uso de filterQueries
• Parámetros básicoso q: texto de la consultao q.op: operador por defecto de la consultao start: offset del conjunto de resultadoso rows: número de elementos a recuperar a partir del starto sort: campo por el que se realiza la ordenaciono fl: campos, separados por coma, que se devolveráno fq: consulta de filtro (ocurrencia múltiple)
Consultando el índice: básicoscurl http://host:8983/opensearch/select/?q=Pilar%20rojo&fl=title,contenttype&start=0&rows=10&wt=xml
<response> <lst name="responseHeader">
<int name="status">0</int><int name="QTime">293</int><lst name="params">
<str name="wt">xml</str><str name="rows">10</str><str name="start">0</str><str name="q">Pilar rojo</str>
</lst> </lst> <result name="response" numFound="2894" start="0">
<doc>...</doc><doc>...</doc><doc>...</doc><doc>
<str name="contenttype">text/html; charset=utf-8</str><str name="title">Pilar Rojo participa nos actos do Xacobeo 2010</str>
</doc><doc>...</doc><doc>...</doc>
</result> </response>
Consultando el índice: avanzada• Selección del RequestHandler
o qt: http://host:8983/opensearch/select?q=Pilar%20rojo&start=0&rows=10&qt=dismax
o Path: http://host:8983/opensearch/dismax?q=Pilar%20rojo&start=0&rows=10
• Utilización de sintáxis de consulta avanzada
o phrase slopping: “jakarta apache”~10
o fuzzy queries: sony~0.9
o wildcarding: c?sa OR casa*
o range queries: ["" to *]
o term boosting: apache^3
• Utilización de SearchComponents
o Faceting: Búsqueda paramétrica (folding de Amazon, eBay, etc)
o Highlight: Resaltado de los términos que concuerdan de la búsqueda
o Spellcheck: Sugerencias de búsqueda con corrección ortográfica
o MoreLikeThis: Documentos relacionados a partir de uno que actúa de patrón.
Consultando el índice: faceting
Consultando el índice: faceting Faceting por valor
o facet: boolean que activa el componenteo facet.field: identifica el campo por el cual se realizará el faceting (ocurrencia múltiple)o facet.prefix: filtra las facetas que comienzan por este valoro facet.sort: "count" para ordenar por valor e "index" para hacerlo alfabeticamenteo facet.limit: el numero limite de facets que se devuelveno facet.offset: offset del conjunto de resultadoso facet.mincount: especifica la cantidad minima referida para que se incluya en un faceto facet.missing: controla si solr debe calcular un recuento de todos los resultados coincidentes
que no tienen ningún valor para el campoo facet.method: selecciona el algoritmo para realizar el facet (enum o fc)
Faceting arbitrarioo facet.query: Especifica una consulta para la que se calcularán los resultados que la matchean
Faceting por Fechao facet.date: nombre del campo sobre el que se realizará el facet tratándolo como fecha (múltiple)o facet.date.start: Margen inferior del rango.o facet.date.end: Margen superior del rango.o facet.date.gap: Expresión que marca el intervalo de fechas que se usará en el facetingo facet.date.hardend: En caso de que el último paso del intervalo sea mayor que facet.date.end se
utilizará dicha fecha como último saltoo facet.date.other: Intervalos que se deben operar a mayores del especificado con before, after,
between, none y all.o facet.date.include: Marca los límites del intervalo con lower, upper, edge, outer, all.
Consultando el índice: faceting<lst name="facet_counts">
<lst name="facet_queries"><int name="locale:"es_ES"">16684</int><int name="locale_americano">844</int>
</lst><lst name="facet_fields">
<lst name="locale"> <int name="es_ES">16684</int> <int name="en_US">844</int>
<int name="ca_ES">110</int> <int name="eu_ES">110</int> <int name="gl_ES">110</int></lst>
</lst><lst name="facet_dates">
<lst name="fecha_resolucion"> <int name="2007-08-31T16:17:37.304Z">4</int> <int name="2008-08-31T16:17:37.304Z">41</int> <int name="2009-08-31T16:17:37.304Z">49</int> <str name="gap">+1YEARS</str> <date name="end">2010-08-31T16:17:37.304Z</date></lst>
</lst></lst>
Consultando el índice: facetingo Faceting simple
/select?q=Cervantes&facet=true&facet.field=category
o Facet múltiple /select?q=Cervantes&facet=true&facet.field=category&facet.field=collections
o Facet con límite y filtrado /select?q=vargas+llosa&facet=true&facet.field=category&facet.limit=10&facet.prefix=Novela
o Facet con mincount y sort /select?q=vargas+llosa&facet=true&facet.field=category&facet.mincount=10&facet.sort=index
o Facet por fechas &facet=true&facet.date.start=-30DAY&facet.date.start=+30DAY&facet.date.gap=+1DAY
o Configuración por campo &facet=true&facet.field=category&facet.field=locale&f.category.facet.mincount=200
o Exclusión de filtros /select?q=vargas&fq={!tag=dt}category:"Novela Histórica"&facet=true&facet.field=category
o Etiquetado /select?q=vargas&facet=true&facet.field={!ex=dt key=Categoría}category
Consultando el índice: highlighting
Parámetroso hl: true para activar el componenteo hl.fl: Campos, separados por comas, donde se efectuará el highlightingo hl.simple.pre: Valor que se situará antes del término matcheadoo hl.simple.post: Valor que se situará después del término matcheadoo hl.snippets: Número de snippets devueltoso hl.fragsize: Número de caracteres que serán devueltos
Ejemplos
o Selección de campos con expresión /select?q=exp&hl=true&hl.fl=descripcion_*
o Código de highlighting personalizado /select?q=exp&hl=true&hl.simple.pre=<span>&hl.simple.post=</span>
o Highlighting de más de 100 caracteres /select?q=exp&hl=true&hl.fragsize=1500
Consultando el índice: highlighting<lst name="highlighting"> <lst name="lp_es_ES2567024">
<arr name="titulo"> <str>La CMT abre <span class="highlight">expediente</span>
sancionador a Proyecto Atarfe</str></arr>
</lst> <lst name="lp_es_ES2574813"> <arr name="asunto">
<str>ACUERDO PARA LA INSCRIPCIÓN DE AUTORIZACIÓN GENERAL EN EL <span class="highlight">EXPEDIENTE</span> AUT-001/98 DE</str></arr><arr name="expediente"> <str><span class="highlight">Expediente</span> AUT</str></arr>
</lst> <lst name="lp_es_ES2627601">
<arr name="asunto"> <str>RESOLUCION DEL ARCHIVO DEL <span class="highlight">
EXPEDIENTE</span> DE LA ENTIDAD GOYA SERVICIOS</str></arr>
</lst> ...</lst>
Consultando el índice: spellchecking Configuración
<searchComponent name="standardSpellcheck" class="solr.SpellCheckComponent"> <str name="queryAnalyzerFieldType">spellFieldType</str>
<lst name="spellchecker"> <str name="name">default</str> <str name="field">SPELL_FIELD</str> <str name="buildOnCommit">true</str> <str name="buildOnOptimize">true</str> <str name="spellcheckIndexDir">./spellchecker</str></lst>
</searchComponent> • Parámetros
o spellcheck: Activa en componenteo spellcheck.count: Número de sugerencias devueltaso spellcheck.dictionary: Selecciona el diccionario a utilizaro spellcheck.onlyMorePopular: Devuelve los elementos con más frecuenciao spellcheck.extendedResults: Añade información adicional a la respuestao spellcheck.collate: Devuelve la frase con todos los términos corregidos
Consultando el índice: spellchecking
<lst name="spellcheck"><lst name="suggestions">
<lst name="ipoteca"><int name="numFound">1</int><int name="startOffset">0</int><int name="endOffset">7</int><arr name="suggestion">
<str>hipoteca</str></arr>
</lst><lst name="vasura">
<int name="numFound">1</int><int name="startOffset">8</int><int name="endOffset">14</int><arr name="suggestion">
<str>basura</str></arr>
</lst><str name="collation">hipoteca basura</str>
</lst></lst>
/select?q=ipoteca%20vasura&spellcheck=true&spellcheck.collate=true&spellcheck.count=1
• ¿Qué són?
• ¿Para qué valen?
• Tiposo solr.PatternReplaceCharFilterFactoro solr.HTMLStripCharFilterFactory
• Configuración <analyzer> <charFilter class="solr.HTMLStripCharFilterFactory"/> <tokenizer class="solr.StandardTokenizerFactory"/> </analyzer>
Análisis personalizado: CharFilterFactories
Análisis personalizado: tokenizers
• ¿Qué són?
• ¿Para qué valen?
• Tiposo solr.WhitespaceTokenizerFactory: Genera tokens a partir de espacios en blancoo solr.KeywordTokenizerFactory: Genera un único tokeno solr.StandardTokenizerFactory: Genera tokens a partir de espacios en blanco y
de los signos de puntuacióno solr.LowerCaseTokenizerFactory: Convierte todo a minúsculas y
elimina cualquier caracter no letra
• Configuración <analyzer> <charFilter class="solr.HTMLStripCharFilterFactory"/> <tokenizer class="solr.StandardTokenizerFactory"/> </analyzer>
Análisis personalizado: analyzer
• ¿Qué són?
• ¿Para qué valen?
• Tiposo solr.SnowballPorterFilterFactory: Genera lexemas a partir del algoritmo
Snowballo solr.StopFilterFactory: Elimina los tokens a partir de una listao solr.SynonymFilterFactory: Genera varios tokens para una misma posicióno solr.LowerCaseFilterFactory: Convierte todos los tokens a minúsculaso solr.ISOLatin1AccentFilterFactory: Genera el token equivalente sin
los caracteres especiales del encoding latin1o solr.EdgeNGramFilterFactory: Genera gramas a partir del comienzo
de cada tokeno solr.RemoveDuplicatesTokenFilterFactory: Elimina tokens
duplicadoso solr.LengthFilterFactory: Elimina los tokens que se excedan o se
queden sean más cortos que los parámetros indicados
Análisis personalizado: analyzer<fieldtype name="teststop" class="solr.TextField"> <analyzer> <tokenizer class="solr.LowerCaseTokenizerFactory"/> <filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/> </analyzer></fieldtype>
<fieldtype name="testedgengrams" class="solr.TextField"> <analyzer> <tokenizer class="solr.LowerCaseTokenizerFactory"/> <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="15" side="front"/> </analyzer></fieldtype>
<fieldtype name="testkeep" class="solr.TextField"> <analyzer> <filter class="solr.KeepWordFilterFactory" words="keepwords.txt" ignoreCase="true"/> </analyzer></fieldtype>
<fieldtype name="syn" class="solr.TextField"> <analyzer> <tokenizer class="solr.WhitespaceTokenizerFactory"/> <filter class="solr.SynonymFilterFactory" synonyms="syn.txt" ignoreCase="true" expand="false"/> </analyzer></fieldtype>
Análisis personalizado: testing
Clustering: replicación
• ¿Qué es la alta disponibilidad?o Master / esclavoo Repetidores
• Limitacioneso Capacidad de indexacióno Tiempo de replicación
• Consideracioneso Replicación pasivao Replicación vía HTTPo Replicación de ficheros
Alta Disponibilidad: replicación
Clustering: replicación
Master
<requestHandler name="/replication" class="solr.ReplicationHandler" > <lst name="master">
<str name="replicateAfter">commit</str> <str name="replicateAfter">startup</str> <str name="replicateAfter">optimize</str> <str name="confFiles">solrconfig_slave.xml:solrconfig.xml,schema.xml</str> </lst>
</requestHandler>
Esclavo
<requestHandler name="/replication" class="solr.ReplicationHandler" > <lst name="slave"> <!– Configurar aquí la URL del servidor maestro --> <str name="masterUrl">http://localhost:8080/solr/replication</str> <str name="pollInterval">00:00:10</str> </lst></requestHandler>
Clustering: sharding
• ¿Qué es el sharding?o Búsqueda distribuidao Indexación distrbuida
• Limitacioneso Hotspottingo Capacidad de crecimientoo IDF distribuido
• Consideracioneso SolrCloudo Replication factoro Autodetección de nodoso Cluster block
Mantenimiento OpenSearch: operaciones
• Backupo Limitaciones
No debe hacerse durante otras operacioneso Copia íntegra del directorio ${dataDir}
• Restauracióno Limitaciones
Evitar índices bloqueados Obligatorio parar el servicio
o Copiar el backup al directorio ${dataDir}
• Optimizeo Limitaciones
Bloquea el directorio Espacio de disco libre equivalente al del índice
o http:/local/opensearch/update?stream.body=%3Coptimize/%3E