English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Esempio di implementazione di indexazione e ricerca con Lucene

0 Introduzione

Con lo sviluppo del World Wide Web e l'arrivo dell'era dei big data, ogni giorno vengono prodotti, archiviati, trasportati e trasformati enormi quantità di informazioni digitali. Come trovare informazioni che soddisfano le nostre esigenze tra queste enormi quantità di informazioni in un modo ragionevole, organizzarle e utilizzarle è una grande sfida. La tecnologia di ricerca full-text è una delle applicazioni di ricerca di informazioni più diffuse oggi, e l'uso dei motori di ricerca nella vita quotidiana, la ricerca di informazioni nei blog e nei forum, i principi di base di queste ricerche sono proprio la tecnologia di ricerca full-text che verrà realizzata in questo articolo. Con la realizzazione della digitalizzazione delle informazioni dei documenti, l'archiviazione efficace delle informazioni e l'estrazione accurata e tempestiva sono le basi che ogni azienda, ente e unità deve fare bene. Per la ricerca full-text in inglese ci sono molte teorie e metodi maturi, il motore di ricerca full-text open source Lucene è un sottoprogetto del progetto Jakarta della Apache Software Foundation, il cui obiettivo è fornire agli sviluppatori di software un kit di strumenti semplice e facile da usare, che faciliti l'implementazione delle funzionalità di ricerca full-text nel sistema di destinazione. Lucene non supporta il cinese, ma ci sono molti tokenizer open source per il cinese che possono creare indici per il contenuto cinese. Questo articolo, basato sulla ricerca dei principi fondamentali di Lucene, ha realizzato rispettivamente la cattura e la ricerca delle pagine web in cinese e inglese.

1 Introduzione a Lucene

1.1 Introduzione a Lucene

Lucene è un pacchetto di strumenti di ricerca full-text scritto in Java, che realizza le due funzionalità fondamentali di costruzione dell'indice e ricerca, e sono indipendenti l'uno dall'altro, il che permette ai sviluppatori di estendere facilmente. Lucene fornisce un'ampia API che permette un facile interagire con le informazioni memorizzate nell'indice. Occorre notare che non è un'applicazione di ricerca full-text completa, ma fornisce funzionalità di indexazione e ricerca per l'applicazione. Quindi, per far funzionare Lucene effettivamente, è necessario effettuare uno sviluppo secondario necessario.

La progettazione strutturale di Lucene è piuttosto simile a quella del database, ma l'indice di Lucene è molto diverso dal database. Entrambi il database e Lucene costruiscono indici per facilitare la ricerca, ma il database è progettato solo per campi specifici e richiede di convertire i dati in informazioni formattate e conservarle. La ricerca full-text invece indexinga tutte le informazioni in un certo modo. Le differenze e le somiglianze tra le due ricerche sono indicate nella tabella 1-1.

Tabella 1-1: Confronto tra ricerca del database e ricerca Lucene

Termine di confronto

Ricerca Lucene

Ricerca del database

Ricerca dei dati

Estrazione dai file di indice di Lucene

Ricerca dei record tramite l'indice del database

Struttura dell'indice

Document (documento)

Record (record)

Risultato della ricerca

Hit: consistente di documenti che soddisfano le relazioni

Set di risultati della ricerca: consistente di record contenenti parole chiave

Ricerca full-text

Supportato

Non supportato

Ricerca vaga

Supportato

Non supportato

Ordinamento dei risultati

Impostare il peso, ordinare per rilevanza

Non può essere ordinato

1.2 Struttura generale Lucene

La forma di distribuzione del pacchetto Lucene è un file JAR, con aggiornamenti di versione rapidi e grandi differenze tra le versioni. Questo documento utilizza la versione 5.3.1, e i sottopacchetti principali utilizzati sono come indicato nella tabella 1-2.

Tabella 1-2: Sottopacchetti e funzionalità

Nome del pacchetto

Funzionalità

Org.apache.lucene.analysis

Segmentazione delle parole

Org.apache.lucene.document

Documenti gestiti dall'indice

Org.apache.lucene.index

Operazioni sull'indice, inclusi l'aggiungere, eliminare ecc.

Org.apache.lucene.queryParser

Query builder, costruzione dell'espressione di ricerca

Org.apache.lucene.search

Gestione della ricerca

Org.apache.lucene.store

Gestione dello storage dei dati

Org.apache.lucene.util

Classe pubblica

1.3 Progettazione architettonica Lucene

Lucene funziona molto bene, ma fondamentalmente, include principalmente due parti: una è la segmentazione delle parole e l'indicizzazione delle parole di testo; l'altra è la restituzione dei risultati in base alle condizioni di ricerca, ovvero la costruzione dell'indice e la ricerca.

如图1-1所示,本文抛出外部接口以及信息来源,重点对网页爬取的文本内容进行索引和查询 。

图1-1:Lucene的架构设计

2 JDK的安装和环境变量的配置

1.jdk的下载:

在oracle官网下载符合系统版本的压缩包,网址如下。点击安装,根据提示进行安装,在安装过程中会提示是否安装jre,点击是。

http://www.oracle.com/technetwork/java/javase/downloads/index.html

2.设置环境变量:

(1)右键计算机=》属性=》高级系统设置=》环境变量=》系统变量=》新建=》JAVA_HOME:安装路径

(2)Path中新增=》%JAVA_HOME%\bin

3.测试是否成功:

开始=》运行=》CMD 回车 在弹出的 DOS 窗口内

输入:java -version 会出现版本信息,

输入: javac出现 javac 的用法信息

出现如图2-1所示为成功。

图2-1:cmd命令框测试java配置

3 编写Java代码实现对网页内容的获取

因为Lucene针对不同语言要使用不同的分词器,英文使用标准分词器,中文选择使用smartcn分词器。在获取网页的时候,先获取网页存为html文件,在html中由于标签  的干扰,会对检索效果产生影响,因此需要对html标签进行剔除,并将文本内容转为txt文件进行保存。中英文除了分词器不同,其他基本一致,因此之后的代码和实验结果演示会选择任一。本文选取五十篇中文故事和英文故事的网页为例。

具体代码设计如下图:Url2Html.java将输入网址的网页转存为html文件,Html2Txt.java文件实现html文档标签的去除,转存为txt文档。具体代码如图3-1和3-2。

public void way(String filePath, String url) throws Exception{
 File dest = new File(filePath); //建立文件
 InputStream is; //接收字节输入流
 FileOutputStream fos = new FileOutputStream(dest); //字节输出流
 URL wangzhi = new URL(url);//Imposta l'URL del sito web
 is = wangzhi.openStream();
 BufferedInputStream bis = new BufferedInputStream(is);//Aggiungi buffer al flusso di input di byte
 BufferedOutputStream bos = new BufferedOutputStream(fos);//Aggiungi buffer al flusso di output di byte
 /*
  * Leggi i byte
  */
 int length;
 byte[] bytes = new byte[1024*20];
 while((length = bis.read(bytes, 0, bytes.length)) != -1){
  fos.write(bytes, 0, length);
 }
 /*
  * Chiudi i flussi di input/output di buffer
  */
 bos.close(); 
 fos.close();
 bis.close();
 is.close();
 }
public String getBody(String val){
		  String zyf = val.replaceAll("</?[^>]+>", ""); // Rimuovi i tag <html>
		  return zyf;
	}
	public void writeTxt(String Str, String writePath) {
		  File writename = new File(writePath);
		  try {
			    writename.createNewFile();
			    BufferedWriter out = new BufferedWriter(new FileWriter(writename));
			    out.write(Str);
			    out.flush();
			    out.close();
		  catch (IOException e) {
			    e.printStackTrace();
		  }
	}

Esempio di pagina web della storia in fiaba "Il lupo stupido va a scuola", il percorso del documento è impostato su "E:\work \lucene \test \data \html" e "E:\work\lucene\test\data\txt", è necessario impostare due parametri ogni volta che si legge una pagina web: il nome del file filename e l'indirizzo del sito web URL. Creare una funzione main per chiamare i due metodi. La implementazione è illustrata nella Figura 3-3:

public static void main(String[] args) {
		    String filename = "jingdizhi"; // Nome del file
		    String url = "http://www.51test.net/show/8072125.html"; // URL della pagina web da estrarre
		    String filePath = "E:\\work\\lucene\\test\\data\\html\\"+filename+".html"; // Percorso del file html+nome del file
		    String writePath = "E:\\work\\lucene\\test\\data\\txt\\"+filename+".txt"; // Percorso del file txt+nome del file
		    Url2Html url2html = new Url2Html();
		    try {
			      url2html.way(filePath, url);
		    } catch (Exception e) {
			      e.printStackTrace();
		    }
		    Html2Txt html2txt = new Html2Txt();
		    String read = html2txt.readfile(filePath); // Leggere il file HTML
		    String txt = html2txt.getBody(read); // Rimuovere i tag HTML
		    System.out.println(txt);
		    try {
			      html2txt.writeTxt(txt, writePath);
		    } catch (Exception e) {
			      e.printStackTrace();
		    }
	  }

Dopo l'esecuzione del programma, viene creato "pazzo_cane_studia.html" e "pazzo_cane_studia.txt" rispettivamente nei due cartelli.

4 Creare l'indice

Il principio di base di indici e query è il seguente:

Creare l'indice: L'indice del motore di ricerca è in realtà una struttura dati specifica di 'matrice parola-documento'. È anche il primo passo per la ricerca full-text. Lucene fornisce la classe IndexWriter per la gestione dell'indice, che include principalmente add(), delete() e update(). C'è anche la configurazione del valore di peso, attraverso la configurazione del valore di peso dell'indice, è possibile restituire in base alla rilevanza durante la ricerca.

进行搜索:原本的直接搜索是针对文档进行顺序检索,在建立索引之后,可以通过对索引的查找以找到索引词在文档中出现的位置,然后返回索引项所对的文档中的位置和词。Lucene提供IndexSearcher类进行对文档的检索,检索形式主要分为两类,第一类是Term,针对单个词项的检索;第二类是Parser,可以自定义构造检索表达式,有较多的检索形式,具体的方法会在之后进行实现的演示。

4.1 实验环境

本PC机采用windows 10x64系统,8G内存,256G固态硬盘。开发环境为Myeclipse 10,jdk版本为1.8。在实验过程中,因为部分语法的转变,若干Class采用1.6版本实现。

4.2 建立索引

建立索引库就是往索引库添加一条条索引记录,Lucene为添加一条索引记录提供了接口,添加索引。

主要用到了“写索引器”、“文档”、“域”这3 个类。要建立索引,首先要构造一个Document 文档对象,确定Document的各个域,这类似于关系型数据库中表结构的建立,Document相当于表中的一个记录行,域相当于一行中的列,在Lucene中针对不同域的属性和数据输出的需求,对域还可以选择不同的索引/存储字段规则,在本实验中,文件名fileName、文件路径fullPath和文本内容content作为Document 的域。

IndexWriter 负责接收新加入的文档,并写入索引库中。在创建“写索引器”IndexWriter 时需要指定所使用的语言分析器。建立索引分为两个类别,第一:无权索引;第二:有权索引。

public Indexer(String indexDir)throws Exception{
 Directory dir=FSDirectory.open(Paths.get(indexDir));
 Analyzer analyzer=new StandardAnalyzer(); // 分析器
 //SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer();
 IndexWriterConfig iwc=new IndexWriterConfig(analyzer);
 writer=new IndexWriter(dir, iwc);
 }

Impostare i campi dell'indice, Store indica se memorizzare il contenuto dell'indice: fileName e fullPath consumano meno memoria e possono essere memorizzati per facilitare la ricerca e il restituzione.

private Document getDocument(File f)throws Exception {
 Document doc=new Document();
 doc.add(new TextField("contents", new FileReader(f)));
 doc.add(new TextField("fileName", f.getName(),Store.YES));
 doc.add(new TextField("fullPath",f.getCanonicalPath(),Store.YES));//Indice di percorso
 return doc;
 }

Dopo l'esecuzione del codice principale, i risultati sono come nella figura: il design restituisce il file "indice file: + percorso del file" quando si indizza un file, e calcola il tempo impiegato per indizzare tutti i file.

4.3 Eliminazione e modifica dell'indice

Le operazioni generali sul database includono CRUD (aggiungere, eliminare, modificare, cercare), aggiungere significa selezionare e creare l'indice, la ricerca come funzione più centrale sarà discussa in seguito, qui principalmente registro i metodi utilizzati per eliminare e aggiornare l'indice.

L'eliminazione si suddivide in due tipi, inclusa l'eliminazione comune e l'eliminazione completa, poiché l'eliminazione dell'indice influisce su tutta la base dati e, per sistemi di grandi dimensioni, l'eliminazione dell'indice significa modificare la parte sottostante del sistema, richiede molto tempo e fatica e non può essere annullata. Quando ho visto l'indice prima di essere creato, sono stati generati diversi file piccoli, quando si cerca, i vari file vengono combinati e cercati. L'eliminazione comune è solo un marchio semplice dell'indice precedentemente creato, che rende impossibile cercare e restituire. L'eliminazione completa distrugge l'indice, non può essere annullata. Prendiamo come esempio l'eliminazione dell'indice dell'elemento "id" con valore "1":

Eliminazione comune (prima della fusione):

writer.deleteDocuments(new Term("id","1"));
writer.commit();

Eliminazione completa (dopo la fusione):

writer.deleteDocuments(new Term("id","1"));
writer.forceMergeDeletes(); // Eliminazione forzata
writer.commit();

Il principio di modifica dell'indice è abbastanza semplice, ovvero implementare la copertura sull'indice esistente, il codice di implementazione è lo stesso dell'aggiunta dell'indice menzionata nel testo precedente, non faccio molte spiegazioni qui.

4.4 Pesaggio dell'indice

Lucene default ordina per rilevanza, Lucene fornisce un parametro di Boosting per Field che può essere impostato, questo parametro viene utilizzato per rappresentare l'importanza della registrazione, quando si soddisfano le condizioni di ricerca, si considerano in primo luogo le registrazioni di alta importanza, i risultati restituiti sono più in alto, se ci sono molte registrazioni, le registrazioni con basso valore di peso vengono posizionate dopo la prima pagina, quindi, l'operazione di pesaggio dell'indice è un fattore importante che influisce sulla soddisfazione dei risultati di ricerca, durante il design del sistema informativo, dovrebbe esserci una formula di calcolo del peso rigorosa, che facilita la modifica del valore di peso di Field, per meglio soddisfare le esigenze degli utenti.

Ad esempio, i motori di ricerca assegnano un peso più alto alle pagine con alto tasso di clic e link in entrata e in uscita, e le ordinano nella prima pagina quando vengono restituiti. Il codice di implementazione è mostrato in Figura 4-1, la comparazione tra senza pesi e con pesi è mostrata in Figura 4-2.

TextField field = new TextField("fullPath", f.getCanonicalPath(), Store.YES);
 if("A GREAT GRIEF.txt".equals(f.getName())){
  field.setBoost(2.0f);//Aggiungi il peso al percorso fullPath del file secondry story.txt;
 }   //Il peso predefinito è 1.0, cambiato in 1.2 per aumentare il peso.
 doc.add(field);

Figura 4-1: Pesaggio dell'indice

Figura 4-2: Prima del pesaggio

Figura 4-2: Dopo il pesaggio

Dal risultato di Figura 4-2 si può vedere che senza pesi, i risultati vengono restituiti in ordine lessicografico, quindi first precede secondry, dopo aver pesato il percorso del file nome secondry, la sequenza di restituzione cambia, testando la funzione di peso.

5 Esegui la ricerca

L'interfaccia di ricerca di Lucene è composta principalmente dai tre classi QueryParser, IndexSearcher e Hits, QueryParser è il parser di query, responsabile della解析用户提交的查询关键字,quando si crea un nuovo parser è necessario specificare il dominio da analizzare e quale analizzatore di linguaggio utilizzare, l'analizzatore di linguaggio utilizzato deve essere lo stesso utilizzato durante la creazione dell'indice, altrimenti i risultati della ricerca non saranno corretti. IndexSearcher è il cercatore di indice, durante la creazione di un'istanza di IndexSearcher è necessario specificare la directory dell'indice, IndexSearcher ha un metodo search per eseguire la ricerca dell'indice, questo metodo accetta Query come parametro e restituisce Hits, Hits è una serie di risultati di ricerca ordinati, gli elementi della serie sono Document. Attraverso il metodo get di Document è possibile ottenere le informazioni del file corrispondente a questo documento, come: nome del file, percorso del file, contenuto del file ecc.

5.1 Ricerca di base

Come mostrato nella query, ci sono principalmente due modi di ricerca, ma si consiglia di utilizzare il primo modo di costruire l'espressione QueryParser, che può avere modi di combinazione flessibili, inclusi espressioni logiche booleane, corrispondenza vaga, ecc., ma il secondo Term può essere utilizzato solo per la ricerca di vocaboli.

1. Costruire l'espressione di ricerca QueryParser:

QueryParser parser = new QueryParser("fullPath", analyzer);
Query query = parser.parse(q);

2. Ricerca di elementi specifici:

Term t = new Term("fileName", q);
Query query = new TermQuery(t);

I risultati della ricerca sono mostrati nella Figura 5-1: Prendiamo come esempio la ricerca dei file di nome fileName che contengono "grande".

Figura 5-1: Risultato della ricerca "grande"

5.2 Ricerca vaga

Durante la costruzione di QueryParser, è possibile ottenere la corrispondenza esatta e vaga modificando il termine q. La corrispondenza vaga può essere modificata aggiungendo "~" dopo "q". Come mostrato nella Figura 5-2:

Figura 5-2: Corrispondenza vaga

5.3 Ricerca con condizioni specifiche

Per la ricerca logica booleana e la ricerca vaga è sufficiente modificare la parola di ricerca q, mentre per la ricerca con condizioni specifiche è necessario impostare l'espressione query, che si suddivide principalmente in alcune categorie principali:

Per la ricerca dell'intervallo di termini specifici, l'intervallo numerico, l'inizio della stringa specifica e la ricerca multi-criterio, elencare le query applicate, il parametro true indica: includere o no i limiti superiori e inferiori.

Specificare l'intervallo di termini:

TermRangeQuery query = new TermRangeQuery("desc", new BytesRef("b".getBytes()), new BytesRef("c".getBytes()), true, true);

Specificare l'intervallo numerico:

NumericRangeQuery<Integer> query = NumericRangeQuery.newIntRange("id", 1, 2, true, true);

Specificare l'inizio della stringa:

PrefixQuery query=new PrefixQuery(new Term("city","a"));

Ricerca a più condizioni:

NumericRangeQuery<Integer>query1=NumericRangeQuery.newIntRange("id", 1, 2, true, true);
PrefixQuery query2=new PrefixQuery(new Term("city","a"));
BooleanQuery.Builder booleanQuery=new BooleanQuery.Builder();
booleanQuery.add(query1,BooleanClause.Occur.MUST);
booleanQuery.add(query2,BooleanClause.Occur.MUST);

5.4 Ricerca evidenziata

Nelle ricerche su motori di ricerca come Baidu, Google e altri, quando viene restituita una pagina web che contiene le parole di ricerca, queste vengono visualizzate in rosso e vengono visualizzate摘要, ovvero viene prelevato e restituito il contenuto che contiene le parole chiave. L'evidenziazione delle ricerche significa modificare lo stile delle parole chiave, nel presente esperimento condotto in myeclipse, i risultati non subiranno cambiamenti di stile, ma aggiungeranno solo tag html alle parole chiave restituite, e se vengono visualizzati sul sito web, produrranno cambiamenti di stile.

L'impostazione dell'evidenziazione è illustrata nella Figura 5-3, i risultati nella Figura 5-4, aggiungerà i tag <b> e <font> ai termini corrispondenti a Nanjing, visualizzandoli come grassetto e rosso sul sito web.

QueryScorer scorer=new QueryScorer(query);
Fragmenter fragmenter=new SimpleSpanFragmenter(scorer);
SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("<b><font color='red'>","</font></b>");
Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer);
highlighter.setTextFragmenter(fragmenter);

Figura 5-3: Impostazione evidenziata

Figura 5-4: Risultato evidenziato

6 遇到实验过程中的问题和不足

La versione di Lucene si aggiorna rapidamente, tra la versione JDK, la versione eclipse e la versione Lucene c'è bisogno di una buona connessione, altrimenti causerà molti incompatibili, ci saranno molte difficoltà nella selezione della versione di debug e nella scelta tra JDK1.6 e JDK1.8, come nel metodo append nella versione 1.8 è stato rimosso e non può essere utilizzato. Ma la lettura del percorso dei documenti FSDirectory.open() richiede JDK1.8 per essere supportato.

I punti deboli di questo esperimento si manifestano principalmente nei seguenti aspetti:

La flessibilità del codice è bassa, durante la estrazione delle pagine web è necessario eseguire manualmente e deve essere eseguita per cinese e inglese separatamente, dovrebbe essere migliorato il codice in modo che il linguaggio delle pagine web possa essere determinato e quindi eseguire automaticamente diversi tokenizzatori diversi.

La riusabilità del codice è bassa, non ci sono classificazioni e costruzioni di metodi ragionevoli, per semplificare, i commenti e i marchi sono realizzati基本上 nei pochi codici core, è necessario migliorare.

La portabilità del codice è bassa, l'estrazione delle pagine web utilizza la versione JDK1.6, l'implementazione di Lucene utilizza la versione JDK1.8, quando si esporta su altri computer, è necessario apportare alcune modifiche e configurazioni dell'ambiente, non può essere realizzato un'operazione one-click.

7 Sintesi

Partendo dai principi di Lucene, ho compreso le idee e i metodi della ricerca full-text, e ho sperimentato e testato le funzioni comuni. Durante l'esperimento, ho compreso i principi dei motori di ricerca, basandomi sul contenuto del corso di ricerca informativa, ho avuto una migliore esperienza pratica. Lucene è una straordinaria framework open-source per la ricerca full-text, attraverso una ricerca approfondita, sono diventato più familiare con il suo meccanismo di implementazione, durante la ricerca ho imparato molti metodi e idee di programmazione orientata agli oggetti, il suo eccellente framework di sistema e l'estensibilità sono degni di studio e riferimento.

Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, il copyright spetta ai rispettivi autori, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, questo sito non detiene il diritto di proprietà, non è stato editato manualmente e non assume responsabilità legali correlate. Se trovi contenuti sospetti di violazione del copyright, sei invitato a inviare una e-mail a notice#oldtoolbag.com (sostituisci # con @ durante l'invio dell'e-mail) per segnalare il problema e fornire prove pertinenti. Una volta verificata, questo sito rimuoverà immediatamente i contenuti sospetti di violazione del copyright.

Ti potrebbe interessare