Sunteți pe pagina 1din 18

Lucene

Lucene 1 este o bibliotecă de funcţii gratuită folosită în căutarea documentară, creată iniţial în Java de Doug Cutting. Crearea ei a fost suportată de fundaţia Apache Software şi este distribuită sub licenţa oferită de Apache Software. În prezent Lucene are support pentru mai multe limbaje de programare, din care amintim Delphi, Perl, C#, C++, Python, Ruby şi PHP.

Potrivită pentru orice aplicaţie care necesită indexare după text şi posibilităţi de căutare, Lucene este recunoscută pentru facilităţile de căutare folosind atât motoarele de căutare de pe Internet cât şi pentru căutarea locală. Librăria Lucene permite doar indexarea şi căutarea şi nu conţine opţiuni de crawling (căutarea automată de pagini web) sau funcţionalităţi de parsare HTML. Proiectul Apache denumit Nutch 2 se bazează pe Lucene şi oferă aceste funţionalităţi, iar proiectul Apache cu numele Solr 3 este un server care pune la dispoziţie facilităţi de căutare bazate pe Lucene.

Sub-proiecte Lucene

Apache Lucene este o bibliotecă performantă, cu numeroase funcţionalităţi în ceea ce priveşte indexarea şi căutarea de text. Proiectul Apache Lucene conţine mai multe sub-proiecte, dintre care amintim:

Lucene Java: este un subproiect dezvoltat în Java ce furnizează funcţii pentru indexare şi căutare;

Nutch: a fost construit pornind de la varianta Lucene Java, oferind în plus suport pentru aplicaţiile ce necesită căutarea pe web;

Hadoop: este o varianta distribuită a proiectului Nutch;

1 http://lucene.apache.org/java/docs/index.html

2 http://en.wikipedia.org/wiki/Nutch

3 http://en.wikipedia.org/wiki/Solr

1

Lucy: este o bibliotecă independentă scrisă în C, derivată din Lucene, cu interfeţe scrise în limbajele Perl şi Ruby;

Solr: este un server performant construit pe baza librăriilor Lucene, cu API-uri suplimentare pentru XML/HTTP şi JSON/Python/Ruby. În plus acesta permite evidenţierea textelor întoarse de motorul de căutare, căutare avansată, caching şi o interfaţă web pentru administrare;

Lucene.Net: este o librărie C# obţinută prin transformarea funcţiilor conţinute de Lucene Java, folosind Microsoft .NET Framework. Acest proiect nu este încă finalizat şi este încă în dezvoltare.

În Figura 1 este prezentat modul de în care funcţiile Lucene pot fi integrate într-o aplicaţie care necesită operaţii de căutare şi indexare. Putem observa modul în care aplicaţia interacţionează cu module Lucene, care sunt responsabile cu indexarea documentelor şi căutarea folosind indexul creat:

cu indexarea documentelor ş i c ă utarea folosind indexul creat: Figura 1. Modul de func

Figura 1. Modul de funcţionare al Lucene

2

Concepte Lucene

Conceptele fundamentale Lucene sunt documentul, câmpul şi termenul (Gospodnetic, Hatcher, 2005). Documentul reprezintă o colecţie de câmpuri. Putem gândi documentul ca un document virtual – o porţiune de date, precum o pagină web, un e-mail, sau un fişier text – pe care vom dori să facem căutări la un moment dat. Sursa iniţială a documentului (precum o bază de date, un document word, un capitol dintr-o carte, şi aşa mai departe) nu este importantă pentru Lucene. Metadatele documentului precum autorul, titlul, subiectul, data ultimei modificări, etc. sunt indexate şi salvate separat ca fiind câmpuri ale documentului. Termenul este unitatea de bază folosită în căutare. Acesta constă din perechi de şiruri de caractere formate din numele câmpului şi valoarea acestui câmp. Aceste perechi sunt create de Lucene în structura sa internă în timpul procesului de indexare, şi prin urmare nu cade în sarcina noastră să le gestionăm.

urmare nu cade în sarcina noastr ă s ă le gestion ă m. Figura 2: Conceptele

Figura 2: Conceptele Lucene

În urma operaţiilor de indexare a unei colecţii de documente după regulile Lucene se obţine un index. Un index stochează informaţii statistice despre termeni şi permite o căutare foarte eficientă bazată pe acestea. Indecşii Lucene se încadrează în categoria

3

indecşilor inverşi deoarece, pentru un termen dat pot fi afişate documentele care conţin termenul, ceea ce e invers ordinii naturale în care documentele afişează termenii.

Pentru o colecţie de documente, presupunând că un document este stocat într-un fişier, pentru a realiza indexarea este nevoie ca fiecare fişier să fie transformat într-un document Lucene populat cu câmpuri conţinând informaţii din fişier.

Un index Lucene poate fi compus din mai mulţi sub-indecşi, numiţi segmente, fiecare din aceştia fiind un index independent şi în care se pot face căutări separate. Indecşii evoluează prin crearea de noi segmente din documente nou adăugate sau în urma unirii unor segmente existente.

Fiecare segment conţine următoarele informaţii:

Nume de câmpuri – reprezintă numele de câmpuri folosite în index.

Valorile stocate în câmpuri – pentru fiecare document, conţine o listă de perechi atribut-valoare, unde atributele sunt nume de câmpuri. Acestea sunt folosite pentru stocarea informaţiilor auxiliare despre document, ca de exemplu titlul, url-ul sau identificatorul pentru acces la o bază de date. Setul de valori de câmpuri stocate este ceea ce întoarce fiecare potrivire la o căutare.

Dicţionar de termeni – conţine toţi termenii folosiţi în toate câmpurile din toate documentele indexate. Dicţionarul conţine de asemenea numărul de documente care conţin termenul şi frecvenţa sa.

Frecvenţa termenilor este calculată pentru fiecare termen din dicţionar în funcţie de numărul documentelor care le conţin şi frecvenţa cu care acestea apar în documentele respective.

Poziţia termenilor este reţinută pentru fiecare termen din dicţionar şi reprezintă poziţiile unde termenul este găsit în documente.

Factori de normalizare sunt calculaţi pentru fiecare câmp din fiecare document, iar aceste valori vor fi multiplicate pentru a calcula numărul de căutari cu succes asupra acelui câmp.

4

Vectorii de termeni sunt construiţi pentru fiecare câmp din fiecare document (numite uneori şi vectorii documentului). Un vector de termeni este format din textul termenilor şi frecvenţa fiecăruia.

Documentele şterse sunt salvate într-un fişier optional.

Interogări Lucene

Interogările pe baza cărora se fac căutările sunt create folosind o listă iniţială de cuvinte. Este important pentru succesul căutărilor ca interogările să fie construite cât mai bine în funcţie de cuvintele care vor fi folosite în căutare. Dacă interogările sunt prea generale atunci vom obţine foarte multe documente, iar relevanţa documentelor rezultate va fi scăzută. În schimb, dacă interogările sunt prea specifice, atunci numărul documentelor întoarse va fi mic, însă va creşte posibilitatea obţinerii documentelor relevante.

O interogare conţine o succesiune de cuvinte (termeni), prefixate sau nu de un operator, formându-se astfel o expresie regulată pe care se bazează căutarea. Există două tipuri de termeni: termeni simpli şi fraze. Termenii simpli sunt reprezentaţi de un singur cuvânt, frazele fiind grupuri de cuvinte cuprinse între ghilimele.

De asemenea trebuie precizat că o interogare Lucene suportă şi precizarea de câmpuri ce trebuie să aibă o anumită valoare, aceasta fiind realizată prin specificarea numelui câmpului urmat de „:” şi valoarea pe care trebuie să o aibă.

De exemplu, pentru a preciza că se caută documente cu titlul „Ernest Hemingway” care să conţină textul „faimos romancier, nuvelist, realizator de povestiri”, interogarea trebuie să aibă forma următoare:

title:”Ernest Hemingway” AND ”faimos romancier, nuvelist, realizator de povestiri”

Aşa cum am precizat anterior, interogările sunt de fapt expresii regulate scrise folosind o anumită sintaxă, şi de aceea este firesc să poată fi folosite în cadrul lor şi

5

simbolurile „?” sau „*” cu acelaşi înteles pe care îl au în toate limbajele de programare care suportă folosirea de expresii regulate. Astfel, „?” înlocuieşte orice caracter şi „*” specifică faptul că pot fi oricâte caractere, inclusiv 0.

Ca operatori booleeni, interogările Lucene acceptă combinaţii folosind următorii operatori: OR, AND, NOT, +, -. Semnificaţiile lor sunt prezentate în continuare:

OR – este operatorul implicit de conjuncţie (dacă nu se foloseşte nici un operator între doi termeni consecutivi, implicit operatorul considerat va fi OR). Acesta leagă doi termeni consecutivi, iar în urma căutării vor fi întoarse documentele care conţin cel puţin unul din cei doi termeni.

AND – caută documentele în care există ambii termeni, fiind echivalentul operatorului de intersecţie de mulţimi.

NOT – exclude din listă documentele care conţin termenul ce urmează după operator, fiind echivalentul operatorului de diferenţă de mulţimi. Este important de ştiut faptul ca acest operator nu poate fi folosit de unul singur într-o interogare, fiind obligatorie prezenţa cel puţin a încă unui termen în afară de cel care urmează lui NOT. De exemplu, o interogare de tipul NOT „Alan Mathison Turing” nu va întoarce nici un rezultat;

+ – specifică fapul că termenul care îi urmează trebuie să apară obligatoriu în fiecare document rezultat;

– specifică faptul că termenul care îi urmează trebuie să nu apară în nici un document rezultat.

Lucene suportă de asemenea în cadrul interogărilor şi gruparea de termeni, aceasta fiind realizată cu ajutorul parantezelor pentru a evita confuziile în legătură cu ceea ce se caută. De exemplu, pentru o interogare cu forma:

(povestire OR istorisire) AND („Conan Doyle” OR Doyle)

6

rezultatele vor conţine unul dintre cuvintele „povestire” sau „istorisireşi unul dintre numele Doyle sau Conan Doyle.

De multe ori însă rezultatele unei interogări nu sunt suficiente sau relevante, de aceea au fost căutate metode de a le îmbunătăţi. Mai jos sunt prezentate câteva strategii folosite de grupul UAIC în construirea sistemelor de întrebare răspuns participante la competiţiile CLEF2007 4 şi CLEF2008 5 :

Pentru o interogare care nu întoarce destule rezultate se poate mări numărul cuvintelor folosite în interogare şi astfel probabilitatea de a găsi un răspuns, prin adăugarea sinonimelor cuvintelor iniţiale din interogare. De exemplu, pentru întrebarea „Ce album a lansat trupa Queen în anul 1991 la casa de discuri EMI?” avem în documentele indexate secvenţa de text „Innuendo este un album al formaţiei Queen, lansat în anul 1991 de casa de discuri EMI.”. Evident, sistemul va întoarce acest fragment de text cu un scor mare dacă interogarea va conţine şi sinonimul cuvântul „trupa”, „formaţia”. Astfel, secvenţa de text de mai sus va căpăta o relevanţă mai mare şi evident astfel se va creşte şi probabilitatea sistemului care va căuta răspunsul în acest fragment de text.

Entităţile de tip nume sau datele calendaristice pe care le folosim în interogare sunt mai importante decât celelate entităţi. Prin urmare, o interogare care va conţine astfel de entităţi va trebui să folosească priorităţi pentru termenii din interogare, iar priorităţile alocate entităţile de tip nume sau pentru datele calendaristice vor fi mai mari.

În urma prelucrării unei întrebări la care se caută răspuns, se identifică focusul întrebării, care reprezintă cel mai important cuvânt folosit în identificarea răspunsului. Acesta trebuie inclus ca fiind obligatoriu în interogare. De cele mai multe ori un articol al cărui titlu este chiar focusul unei întrebări va conţine răspunsul la acea întrebare. De aceea, adăugarea opțională a apariţiei focusului în titlu va duce la creşterea şanselor găsirii răspunsului corect în secvenţele cu relevanţă mare.

Pentru o întrebare de tip DEFINIŢIE (în care răspunsul căutat reprezintă o definiţie) de multe ori lista de cuvinte folosite pentru a construi interogarea este restrânsă.

4 CLEF2007: http://www.clef-campaign.org/2007.html

5 CLEF2008: http://www.clef-campaign.org/2008.html

7

De aceea, pentru a restrânge numărul de secvenţe de text întoarse de o căutare trebuie adăugate verbele care marchează o definiţie: a fi, a defini, a reprezenta, a arăta. De asemenea, ca la cazul anterior adăugarea optională a condiţiilor ca în titlu să avem cuvintele care apar în întrebare va duce la creşterea şanselor găsirii răspunsului corect în secvenţele cu relevanţă mare.

În urma aplicării acestor îmbunătăţiri, o interogare a sistemelor de întrebare- răspuns construite va căuta documente cu referinţă obligatorie la focusul întrebării, la entităţile de tip nume şi la datele calendaristice, şi cu referinţă opţională la sinonimele cuvintelor folosite în interogare, iar în cazul întrebărilor de tip definiţie vor fi căutate documente în care sa apară obligatoriu atât focusul întrebării cât şi unul dintre verbele „a fi”, „a defini”, „a reprezenta”, „a arăta”. De asemenea, s-au adăugat opţiuni de căutare a documentelor care au ca titlu focusul întrebării sau cuvintele care apar în întrebările de tip definiţie.

Scorul Lucene

Relevanţa unui document obţinut de Lucene pornind de la o interogare a utilizatorului este un număr cuprins între 0 şi 1 şi este calculată cu ajutorul unei combinaţii dintre Modelul Spaţiu Vector (VSM) al Regăsirii Informaţiilor şi Modelul Boolean. Ideea generală a VSM este următoarea: cu cât creşte numărul de apariţii ale unui termen din interogare într-un document relativ la numărul de apariţii ale termenului în toate documentele din colecţie, cu atât creşte relevanţa documentului respectiv în ceea ce priveşte interogarea. Modelul Boolean este folosit pentru a scădea numărul documentelor pentru care trebuie calculată relevanţa, pe baza folosirii logicii booleene în specificaţiile interogării. De asemenea, Lucene rafinează acest model prin metode proprii şi mai trebuie menţionat faptul că în calculul relevanţei se face o diferenţă clară în modul de indexare a colecţiei, deci indexări realizate diferit vor întoarce relevanţe diferite pentru documente pe care s-a făcut aceeaşi căutare.

8

Formula generală de calculare a scorului de bază Lucene este dată mai jos:

calculare a scorului de baz ă Lucene este dat ă mai jos: Acest scor este calculat

Acest scor este calculat pentru fiecare document în parte, iar documentele care au această valoare peste 0, vor fi documentele care s-au potrivit căutării în index. În faza următoare se identifică documentul cu scorul cel mai mare. În caz că acest scor este peste valoarea 1 se normalizează toate scorurile în funcţie de această valoare, astfel încât să avem toate scorurile documentelor care s-au potrivit cel mult 1.

În tabela de mai jos sunt explicate elementele care apar în formula de calculare a scorului Lucene.

Element

Descriere

tf(t in d)

Frecvenţa termenului (t) în documentul (d)

idf(t)

Inversul frecvenţei termenului (t) în document

boost(t.field in d)

Valoarea asociată câmpului în momentul indexării

lengthNorm(t.field in d)

Valoarea normalizată a câmpului, care a fost obţinută folosind numărul de termeni din cadrul câmpului. Această valoare este calculată în momentul indexării şi este salvată în index.

coord(q, d)

Factorul de coordonare este calculat în funcţie de termenii documentului care pot fi folosiţi în interogare.

queryNorm(q)

Valoarea normalizată pentru o interogare, folosind suma pătratelor ponderilor asociate termenilor interogării.

9

Clasele de bază Lucene

Să aruncăm o privire la clasele de bază ce sunt folosite de motorul de indexare şi căutare Lucene.

Document – Clasa Document este folosită pentru a reprezenta un document în Lucene. Vom indexa obiecte Document şi vom obţine obiecte Document ca rezultat al căutărilor.

Field – Clasa Field reprezintă o secţiune a clasei Document. Obiectul de tip Field va conţine un nume pentru secţiune şi datele curente.

Analyzer – Clasa Analyzer este o clasă abstractă ce oferă o interfaţă care va fi preluată de clasa Document şi va fi transformată în termeni ce pot fi indexaţi. Sunt câteva implementări foarte utile ale acestei clase, dar cea folosită mai des este clasa StandardAnalyzer.

IndexWriter – Clasa IndexWriter este folosită pentru a crea şi a administra indecşii.

IndexSearcher – Clasa IndexSearcher este folosită pentru a căuta în cadrul unui index.

QueryParser – Clasa QueryParser este folosită pentru a construi un parser ce poate căuta în cadrul unui index.

Query – Clasa Query este o clasă abstractă ce conţine criteriul de căutare creat de clasa QueryParser.

Hits – Clasa Hits conţine obiecte Document ce sunt întoarse de execuţia obiectului Query folosind indexul.

10

Indexarea unui Document

Primul pas constă în instalarea aplicaţiei Lucene. Acest lucru se face foarte simplu urmând instrucţiunile de pe pagina de start 6 .

Să presupunem ca vrem să scriem un program cu ajutorul căruia profesorii să-şi poată încărca în indexul Lucene articolele proprii. Programul trebuie să permită ca profesorii să-şi introducă numele, titlul articolului, şi să selecteze dintr-o listă de categorii ce descriu articolul. Vom presupune de asemenea că acest program va depozita articolul într-un spaţiu accesibil de pe web. Pentru a indexa acest articol vom avea nevoie de articol, numele autorului, data când acesta a fost scris, subiectul articolului, titlul articolului, şi adresa URL unde este acesta localizat. Cu aceste informaţii putem construi un program ce poate indexa în mod eficient o colecţie de articole şi care să ne permită mai apoi regăsirea uşoară în această colecţie.

În continuare prezentăm structura clasei de bază ce va face indexarea incluzând de asemenea şi importurile de care avem nevoie.

Scheletul clasei de bază incluzând şi importurile

import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.Hits;

import java.util.Date;

public class ArticleIndexer {

6 http://lucene.apache.org/

11

}

Primul lucru pe care e nevoie să-l facem este să transforma articolul nostru într-un obiect de tip Document.

găsim o modalitate de a

Metoda de a crea un Document dintr-un articol

private Document createDocument(String article, String author, String title, String topic, String url, Date dateWritten) {

Document document = new Document(); document.add(Field.Text("author", author)); document.add(Field.Text("title", title)); document.add(Field.Text("topic", topic)); document.add(Field.UnIndexed("url", url)); document.add(Field.Keyword("date", dateWritten)); document.add(Field.UnStored("article", article)); return document;

}

Mai întâi se creează un obiect de tip Document, care va fi asociat fiecărui articol pe care vrem să-l indexăm. După care trebuie completate cîmpurile articolului din cadrul clasei Document. Numele considerate sunt la întâmplare şi funcţionează precum cheile dintr-un HashMap. În cazul nostru am considerat cîmpurile “author”, “title”, “topic”, “url”, “date” şi “article”, şi le-am completat cu valorile corespunzătoare articolului pe care vrem să-l indexăm. Perechile (câmp, valoare) reprezintă termenii documentului current. Singura condiţie este ca numele folosite să fie de tip String. Metoda de adăugare add a unui Document va lua un obiect de tip Field pe care-l vom construi folosind una din metodele statice din clasa Field. Există patru metode disponibile pentru a adăuga obiecte de tip Field la un Document.

Field.Keyword – Informaţia este memorată şi indexată, dar nu este tokenizată. Aceasta este foarte utilă pentru informaţiile ce trebuie scrise fără a fi modificate

12

precum sunt cele de tip dată. De fapt, Field.Keyword poate considera un obiect de tip Date ca intrare.

Field.Text – Informaţia este memorată, indexată şi tokenizată. În câmpurile Field.Text nu trebuie folosite pentru cantităţi mari de date, cum este de exemplu conţinutul articolului, deoarece indexul va creşte foarte mult întrucât va conţine o copie completă a articolului, plus o variantă tokenizată.

Field.UnStored – Informaţia nu este scrisă, dar este indexată şi tokenizată. Cantităţile mari de date precum conţinutul articolului trebuie plasate în acest câmp.

Field.UnIndexed – Informaţia este scrisă, dar nu este indexată sau tokenizată. Această informaţie este folosită ca dată pe care vrem s-o obţinem ca rezultat al căutării, fără a avea posibilitatea de a căuta în ea. În exemplu nostru, deoarece nu vrem să permitem căutarea în URL-ul articolului, nu există motive să-l indexăm, dar îl vom dori ca rezultat atunci când găsim un rezultat al căutării.

Acum

având

un

obiect

de

tip

Document,

avem

nevoie

să

considerăm

un

IndexWriter pentru a scrie acest Document în index.

Indexarea Multicriterială

Din modul de funcţionare al motorului Lucene nu avem restricţii din punct de vedere al numărului câmpurilor pe care le adăugăm la index, şi de asemenea nu avem restricţii a modului în care adăugăm obiectele de tip Field la un Document. Prin urmare este posibil să adăugăm mai multe câmpuri pe care să le folosim în procesul de căutare în index. Pentru aplicaţia noastră, deja am adăugat câmpurile “author”, “title”, “topic”, “url”, “date” şi “article”, dar pe lângă acestea putem adăuga şi alte câmpuri pe care să le folosim în procesul de căutare în index cum ar fi lista referinţelor din articolul curent, abstractul articolului sau cuvintele cheie.

Ce este important de ştiut este faptul că atunci când vom folosi indexul în procesul de căutare, va trebui să specificăm care din aceste câmpuri este folosit implicit

13

în procesul de căutare. Vom vedea în continuare cum se specifică acest câmpul şi modul în care putem căuta după mai multe câmpuri în acelaşi timp.

Metoda de a adăuga un Document la index

String indexDirectory = "lucene-index";

private void indexDocument(Document document) throws Exception { Analyzer analyzer = new StandardAnalyzer(); IndexWriter writer = new IndexWriter(indexDirectory, analyzer,

false);

 

writer.addDocument(document);

writer.optimize();

writer.close();

}

Vom crea mai întâi un StandardAnalyzer şi apoi pe baza acestuia vom creea un IndexWriter. În constructor vom specifica directorul unde va fi creat indexul. Valoarea booleană de la sfârşitul constructorului îi va spune lui IndexWriter fie că va trebui să creeze un nou index, fie că va trebui să adauge la un index existent. Când se adaugă un nou document la un index existent va trebui să specificăm false. Apoi, vom adăuga obiectul Document la index. La sfârşit, se optimizează şi se închide indexul. Dacă se vor adăuga mai multe obiecte Document, va trebui ca de fiecare dată să optimizăm şi să închidem indexul, până când toate obiectele Document vor fi adăugate la index.

Mai jos este metoda care pune la un loc toţi aceşti paşi.

Metoda care face indexarea

public void indexArticle(String article, String author, String title, String topic, String url, Date dateWritten) throws Exception { Document document = createDocument(article, author,

14

indexDocument(document);

}

title, topic, url, dateWritten);

Prin execuţia acestui cod pentru un articol el va fi adăugat la index. Prin modificarea valorii booleene din constructorul IndexWriter la true, se va creea un nou index. Prin urmare acest lucru trebuie făcut prima dată când se crează indexul sau de fiecare dată când se doreşte reconstruirea lui. Odată indexul terminat de construit, se poate căuta un articol.

Căutarea în Index

Am adăugat articolele noastre la index şi acum dorim să le căutăm. Presupunând că am creat şi o interfaţă prieteneasă pentru utilizatorii noştri, trebuie doar să luăm cererea de la un utilizator şi să o rulăm folosind indexul creat de noi. Deoarece am adăugat câteva tipuri de câmpuri, utilizatorii programului pot folosi diverse creiterii de căutare. După cum vom vedea, vom putea specifica care câmp este implicit folosit la căutare, dar de asemenea vom vedea cum vom putea căuta în orice câmpuri care se află în index.

Codul necesar efectuării căutării este prezentat mai jos.

Căutarea în index – câmpul searchCriteria va fi oferit de utilizator

IndexSearcher is = new IndexSearcher(indexDirectory); Analyzer analyzer = new StandardAnalyzer(); QueryParser parser = new QueryParser("article", analyzer); Query query = parser.parse(searchCriteria); Hits hits = is.search(query);

15

Cu toate că sunt implicate o mulţime de clase, operaţia de căutare este relativ simplă. Primul lucru pe care trebuie să-l realizăm este de a creea un obiect IndexSearcher care foloseşte directorul în care articolele au fost indexate. După aceea se creează un obiect StandardAnalyzer. Obiectul StandardAnalyzer este trimis constructorului unui QueryParser împreună cu numele câmpului implicit folosit în căutare. Acesta va fi câmpul ce va fi folosit implicit dacă utilizatorul nu va specifica un câmp în criteriul de căutare. În cazul nostru câmpul după care se face căutarea implicită este article. Apoi se parsează criteriul curent de căutare introdus de utilizator, obţinând un obiect de tip Query. În continuare se execută obiectul de tip Query folosind obiectul de tip IndexSearcher. Acesta va întoarce un obiect Hits, care este o colecţie a tuturor articolelor ce verifică criteriul de căutare specificat.

Extragerea obiectelor Document din obiectul Hits se face folosind metoda doc() a obiectului Hits.

Extragerea obiectelor Document

for (int i=0; i<hits.length(); i++) { Document doc = hits.doc(i); // display the articles that were found to the user

}

is.close();

Clasa Document are o metodă get() care poate fi utilizată pentru a extrage informaţiile ce au fost scrise în index. De exemplu, pentru a obţine autorul dintr-un obiect de tip Document ar trebui să apelăm metoda doc.get("author"). Deoarece am adăugat chiar articolul ca un câmp Field.UnStored, încercarea de a-l accesa folosind metoda get ne va întoarce null. Oricum, deoarece am adăugat URL-ul unui articol la index, vom putea să-l identificăm şi să-l afişăm utilizatorului în lista cu rezultatele. Va trebui ca de fiecare dată să închidem IndexSearcher după ce am terminat extragerea tuturor obiectelor de tip Document. Încercările de a extrage un Document după închiderea acestuia va genera o eroare:

java.io.IOException: Bad file descriptor

16

Specificarea criteriilor de căutare

Lucene oferă o mulţime de moduri în care putem face căutarea, incluzând interogări care conţin AND OR şi NOT, căutări fuzzy, căutari în vecinătate, căutări cu caractere speciale, şi căutări în domeniu. Să vedem câteva exemple:

Exemplul 1: să căutăm articolele profesorului Henry ce conţin informaţii despre relativity şi quantum physics. Interogarea Lucene va fi:

author:Henry relativity AND "quantum physics"

cu semnificaţia: se caută Henry în câmpul author, iar în câmpul article se caută relativity şi "quantum physics" (reamintim că implicit căutarea se face după câmpul article folosit în constructorul QueryParser). După cum observăm putem face simultan căutarea în două câmpuri ale indexului creat: author şi article.

Exemplul 2: să identificăm articolele ce conţin fraza ”string theoryşi nu conţin Einstein. Interogarea Lucene este mai jos:

"string theory" NOT Einstein

Exemplul 3: să căutăm articolele care conţin Kepler la cel mult 5 cuvinte de Galileo:

"Galileo Kepler"~5

Exemplul 4: să găsim articolele profesorului Johnson scrise în January anul 2008:

author:Johnson date:[01/01/2008 TO 01/31/2008]

După cum am văzut dacă nu specificăm un câmp atunci căutarea s-a făcut după câmpul article. De asemenea, am putut să facem căutări după orice câmp din Document atâta timp cât acesta a fost adăugat ca Field.UnIndexed.

17

Concluzii

Lucene este un motor de căutare care, deşi utilizeată tehnici complexe de indexare şi regăsire, este extrem de uşor de folosit. O facilitatea importantă a motorului Lucene vine din faptul că putem adăuga oricâte câmpuri la index pe care le putem folosi mai apoi în procesul de căutare.

Lucene nu caută automat în documentele noastre, dar oferă o metodă pentru scrierea propriilor noastre căutări. Cu toate că Lucene suportă doar text simplu, există disponibile clase Java care pot transforma în text simplu documente HTML, XML, documente Word, şi fişiere PDF. Majoritatea acestor clase sunt disponibile pornind de la situl web Lucene.

Bibliografie

Gospodnetic, O., Hatcher, E. 2005. Lucene in Action. Manning Publications Co.

Paul, T. 2004. The Lucene Search Engine: Adding search to your applications.

http://www.javaranch.com/journal/2004/04/Lucene.html

Links

Lucene Resources: http://wiki.apache.org/jakarta-lucene/Resources

18