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

Luceneでインデックスとクエリを実現する実例解説

0序説

ウェブの発展とビッグデータ時代の到来に伴い、毎日大量のデジタル情報が生産、保存、伝送、変換されています。大量の情報から自分のニーズに合った情報を見つけ、それを整理して利用するのは大きな課題です。全文検索技術は現在、最も一般的な情報検索アプリケーションであり、検索エンジンを利用してブログやフォーラムで情報を探す際の核心原理が、この文書で実現される全文検索技術です。文書情報のデジタル化の実現に伴い、情報を効果的に保存し、迅速かつ正確に抽出することは、どの企業や組織も基礎として行うべきことです。英語の全文検索には多くの成熟した理論と方法があります。オープンソースの全文検索エンジンLuceneは、Apacheソフトウェア財団のJakartaプロジェクトグループのサブプロジェクトであり、開発者に簡単で使いやすいツールキットを提供し、ターゲットシステムで全文検索機能を実現するために便利です。Luceneは中国語をサポートしていませんが、現在多くのオープンソースの中国語分かち書きエンジンが中国語コンテンツのインデックス作成に対応しており、この文書ではLuceneの核心原理を研究した上で、中国語と英語のウェブページのスクレイピングと検索を実現しました。

1 Luceneの紹介

1.1 luceneの概要

LuceneはJavaで書かれた全文検索エンジンツールパッケージであり、索引構築と検索の二つの主要機能を実現しています。これらは相互独立しており、開発者が簡単に拡張できるように設計されています。Luceneは豊富なAPIを提供しており、索引に保存された情報と簡単にインタラクションできます。ただし、これは完全な全文検索アプリケーションではなく、アプリケーションに索引と検索機能を提供するためのものです。Luceneが実際に機能するためには、その上で必要な二次開発を行う必要があります

Luceneの構造設計はデータベースの設計と比較して非常に似ていますが、Luceneの索引とデータベースは非常に異なります。データベースとLuceneが索引を構築するのは検索を簡単にするためですが、データベースは特定のフィールドにのみ適用され、データを形式化された情報に変換し保存する必要があります。一方、全文検索はすべての情報を一定の方法で索引付けます。この二つの検索の違いと類似点は以下の通り1-1示されています。

テーブル1-1:データベース検索とLucene検索の比較

比較項

Lucene検索

データベース検索

データ検索

Luceneの索引ファイルから検出します

データベースの索引からレコードを検索します

索引構造

ドキュメント(文書)

レコード(記録)

クエリ結果

ヒット:関係を満たすドキュメントから構成されています

クエリ結果セット:キーワードを含むレコードから構成されています

全文検索

サポートしています

サポートしていません

模糊查询

サポートしています

サポートしていません

結果ソート

ウェイトを設定し、関連性ソートを行います

ソートできません

1.2 luceneの全体構造

Luceneソフトウェアパッケージのリリース形式はJARファイルであり、バージョンアップが速くバージョン間の差が大きい。本文では以下のものを使用しています5.3.1のバージョン、主に使用されるサブパッケージは以下の通り1-2示されています。

テーブル1-2:サブパッケージおよび機能

パッケージ名

機能

Org.apache.lucene.analysis

分詞

Org.apache.lucene.document

インデックス管理のドキュメント

Org.apache.lucene.index

インデックス操作、追加、削除など

Org.apache.lucene.queryParser

クエリエンジン、検索表現の構築

Org.apache.lucene.search

検索管理

Org.apache.lucene.store

データストレージ管理

Org.apache.lucene.util

公共クラス

1.3 luceneアーキテクチャ設計

Luceneは非常に強力ですが、根本的には以下の二つの主要な部分に分類されます:一つはテキスト内容を分詞してインデックスに保存すること、もう一つはクエリ条件に基づいて結果を返すこと、つまりインデックス作成およびクエリの二つの部分です。

図に示されるように1-1図に示されるように、本文では外部インターフェースおよび情報源を提案し、特にウェブクロールのテキスト内容のインデックス作成およびクエリを実行することに焦点を当てています。

図1-1:Luceneのアーキテクチャ設計

2 JDKのインストールと環境変数の設定

1JDKのダウンロード:

Oracleのウェブサイトからシステムバージョンに対応する圧縮パッケージをダウンロードします。以下のURLをクリックしてインストールし、インストール中に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ファイルとして保存する必要があります。中国語と英語は分詞器が異なる以外は基本的に同じであり、したがって、後のコードと実験結果のデモはどちらを選択するかを選択します。本文では、五十篇の中国語の物語と英語の物語のウェブページを例として挙げます。

具体代码设计如下图:Url2具体的なコード設計は以下の図に示されています:Url2Html.javaは入力URLのウェブページをhtmlファイルとして保存し、Html3-1Txt.javaファイルはhtmlドキュメントのタグを除去し、txtドキュメントとして保存します。具体的なコードは図に示されています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);
 URLを設定します
 is = wangzhi.openStream();//BufferedInputStream bis = new BufferedInputStream(is);
 BufferedOutputStream bos = new BufferedOutputStream(fos);//バイト出力ストリームにバッファを追加します
 /*
  * バイトを読み取ります
  */
 int length;
 byte[] bytes = new byte[1024*20];
 while((length = bis.read(bytes, 0, bytes.length)) != -1{
  fos.write(bytes, 0, length);
 }
 /*
  * バッファストリームと入出力ストリームを閉じます
  */
 bos.close(); 
 fos.close();
 bis.close();
 is.close();
 }
public String getBody(String val){
		  String zyf = val.replaceAll("<",/?[^>]+">", ""); //《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();
		  }
	}

童話「のんびり狼が学校に行く」のウェブページを例に、ドキュメントパスは「E:\work \lucene \test \data \html」と「E:\work\lucene\test\data\txt」に設定し、ウェブページを読み込むたびに設定する必要のある2つのパラメータはファイル名filenameと目標URLurlです。main関数を新規作成し、2つのメソッドの呼び出しを実現します。具体的な実装は図に示されています3-3所示:

public static void main(String[] args) {
		    String filename = "jingdizhi";//ファイル名
		    String url = "http://www.51test.net/show/8072125.html";//収集する必要のあるウェブページのURL
		    String filePath = "E:\\work\\lucene\\test\\data\\html\\"+filename+.html;//htmlのファイルパスを出力する+ファイル名
		    String writePath = "E:\\work\\lucene\\test\\data\\txt\\"+filename+.txt;//txtのファイルパスを出力する+ファイル名
		    Url2Html url2html = new Url2Html();
		    try {
			      url2html.way(filePath,url);
		    } catch (Exception e) {
			      e.printStackTrace();
		    }
		    Html2Txt html2txt = new Html2Txt();
		    String read=html2txt.readfile(filePath);//htmlファイルを読み込む
		    String txt = html2txt.getBody(read);//htmlタグを削除する
		    System.out.println(txt);
		    try {
			      html2txt.writeTxt(txt,writePath);
		    } catch (Exception e) {
			      e.printStackTrace();
		    }
	  }

プログラムを実行すると、それぞれのフォルダーに「ぼくろうずががくび.html」と「ぼくろうずががくび.txt」が作成されます。

4 索引の作成

索引とクエリの基本原理は以下の通りです:

索引の作成:検索エンジンの索引は「単語-「ドキュメント行列」の具体的なデータ構造。これは全文検索の最初のステップでもあります。LuceneはIndexWriterクラスを提供して索引の管理を行い、add()、delete()、update()などの主要な機能を提供します。また、権重の設定を行い、異なる索引の権重を設定することで、検索時に関連性の大きさに応じて結果を返すことができます。

検索を実行します:元の直接検索はドキュメントに対する順序検索であり、索引が作成された後、索引を検索して索引語がドキュメントにどの位置に出现しているかを確認し、その位置と語に対応するドキュメントの位置を返します。LuceneはIndexSearcherクラスを提供してドキュメントの検索を行い、検索形式は主に二つのカテゴリーに分けられます、一つ目はTermで、単一の語項の検索に対応し、二つ目はParserで、検索表現をカスタマイズして構築することができます。具体的な方法は後で実現のデモンストレーションを行います。

4.1 実験環境

このPC機はWindowsを使用しています。 10x64OS、8Gメモリ、256G SSDハードディスク。 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);
 }

インデックスフィールドの設定、Storeはインデックス内容の保存を示します:fileNameとfullPathはメモリ使用量が少ないため、保存し、検索時に返却するために便利です。

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));//パスインデックス
 return doc;
 }

メインコードを実行した後の結果は以下の通りです:インデックスのファイルに対してファイル“インデックスファイル:+ファイルパス

4.3 インデックスの削除と変更

データベースの操作は一般的にCRUD(追加、削除、変更、検索)に分かれており、追加はインデックス項目の選択と作成を意味します。検索は比較的核心の機能であり、後で詳しく説明しますが、ここでは削除やインデックスの更新時における方法を記録します。

削除は二つのタイプに分かれており、一般的な削除と完全削除があります。インデックスの削除は全体のデータベースに影響を与え、大規模なシステムでは、インデックスの削除はシステムのベースレベルの変更を意味し、時間と労力がかかり、戻すことができません。以前のインデックスの部分で、インデックスが生成された後にいくつかの小さなファイルが生成されることを見ました。検索を行う際には、各ファイルを統合して検索を行います。一般的な削除は、以前に作成されたインデックスに対して単純なマークを付けただけで、検索が行えないようにしています。完全削除はインデックスを破棄し、元に戻すことはできません。インデックス項目“id”の削除は以下のようになります。1のインデックスを例に示します:

通常の削除(マージ前に削除):

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

完全な削除(マージ後に削除):

writer.deleteDocuments(new Term("id","1)));
writer.forceMergeDeletes(); // 強制的な削除
writer.commit();

インデックスの変更の原理は比較的単純で、既存のインデックスに上書きして実現します。実装コードは前のインデックスの追加と同じです。ここでは詳細に説明しません。

4.4 インデックスの重み付け

Luceneはデフォルトで関連性に基づいてソートします。LuceneはBoostingパラメータを提供しており、このパラメータはレコードの重要性を示します。検索条件を満たす場合、重要性の高いレコードを優先し、結果が上位に表示されます。レコードが多い場合、低い重みのレコードは最初のページの後ろに表示されます。したがって、インデックスの重み付け操作は、返される結果の満足度に影響を与える重要な要因です。実際の情報システムの設計では、厳格な重み計算式が必要であり、Fieldの重み値の変更を容易にし、ユーザーのニーズをよりよく満たすことができます。

例えば、検索エンジンはクリック率が高い、リンクが入ってくるページやリンクを外に出るページに高い重みを付け、返される際には最初のページに表示されます。実装コードは図4-1示されています。重み付けなしと重み付けされた結果の比較は図4-2示されています。

TextField field = new TextField("fullPath", f.getCanonicalPath(), Store.YES);
 if("A GREAT GRIEF.txt".equals(f.getName())){
  field.setBoost(2.0f);//secondry story.txtのfullPathパスに重み付けを行います;
 }   //デフォルトの重みは1.0、を1.2つまり、重みを増やします。
 doc.add(field);

図4-1:インデックス重み付け

図4-2:重み付け前

図4-2:重み付け後

図4-2結果から分かるように、重み付けなしでは、辞書順にソートして返されます。したがって、firstがsecondryよりも前に、secondryに命名されたファイルパスに重み付けが適用されると、返される際に順序が変更され、重み付けのテストが実行されます。

5 クエリを実行します

Luceneの検索インターフェースは主にQueryParser、IndexSearcher、Hitsによって構成されています3 由个类构成,QueryParser 是查询解析器,负责解析用户提交的查询关键字,在新建一个解析器时需要指定要解析的域和使用什么语言分析器,这里使用的语言分析器必须与索引库建立时使用的解析器相同,否则查询结果不正确。IndexSearcher是索引搜索器,在实例化IndexSearcher时需要指定索引库所在的目录,IndexSearcher有一个search 方法执行索引的检索,这个方法接受Query 作为参数,返回Hits,Hits 是一系列排好序的查询结果的集合,集合的元素是Document。通过Document的get 方法可以得到与这个文档对应文件的信息,比如:文件名、文件路径、文件内容等。

5.1 基本查询

如图查询主要有两种方式,但是推荐使用第一种构造QueryParser表达式,它可以有灵活的组合方式,包括布尔逻辑表达、模糊匹配等,但是第二种Term只能针对词汇查询。

1.构造QueryParser查询式:

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

2.对特定项的查询:

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

查询结果如图5-1所示:以查询文件名fileName包含“大”为例。

図5-1:“大”查询结果

5.2 模糊查询

在构造QueryParser时,通过对词项q的修改可以实现精确匹配和模糊匹配。模糊匹配通过在“q”之后加“~”进行修改。如图5-2所示:

図5-2:模糊匹配

5.3 限定条件查询

布尔逻辑查询和模糊查询只需要对查询词q进行更改,而限定条件查询需要对query表达式进行设定,主要分为以下几类:

分别为指定项范围搜索、指定数字范围、指定字符串开头和多条件查询,分别列出应用的查询,true参数指的是:是否包含上限和下限在内。

指定项范围:

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

指定数字范围:

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

指定字符串开头:

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

多条件查询:

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 高亮查询

在百度、谷歌等搜索引擎中,进行查询时,返回的网页包含查询关键字的时候会显示为红色,且进行摘要显示,即对包含关键字的部分内容进行截取并返回。高亮查询即为实现对关键字的样式更改,本实验在myeclipse中进行,返回结果并不会有样式的改变,只会对返回内容的关键字添加html标签,如果显示到网页即产生样式的变化。

高亮的设置代码如图5-3示,结果如图5-4示,会对南京匹配词添加<b>和<font>标签,显示到网页上为加粗和变红。

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);

図5-3:ハイライト設定

図5-4:結果のハイライト表示

6 実験中に直面した問題と欠点

Luceneのバージョンは更新が速く、jdkバージョン、eclipseバージョン、Luceneバージョン間に良い連携が必要であり、そうでないと多くの不適合が生じます。デバッグバージョンおよびjdk1.6とjdk1.8の選択において多くの困難が生じます。例えば、ウェブページのクロールにおけるappendメソッドは、1.8バージョンは削除され、使用できません。しかし、ドキュメントパスの読み取りFSDirectory.open()にはjdkが必要です。1.8がサポートしています。

この実験の欠点は主に以下の通りです:

コードの柔軟性は低く、ウェブページのクロールには手動で行い、中国語と英語に分けて行う必要があります。ウェブページの言語を判定し、自動的に異なる分詞器を選択するようにコードを完璧にする必要があります。

コードの再利用性は低く、合理的な分類やメソッドの構築がなく、簡単にするために、基本的に核心コードにコメントやマークを付けながら実現しています。改善の余地があります。

コードの移植性は低く、ウェブページのクロールにはjdkを使用しています。1.6のバージョンでは、Luceneの実現が使用するのはjdkです。1.8のバージョンでは、他のマシンにエクスポートする際に、環境の多少の修正と設定が必要であり、一括操作を実現できません。

7 まとめ

この記事はLuceneの原理から始まり、全文検索の考え方と方法を学び、常用機能の実験とテストを行いました。実験の過程で、検索エンジンの原理を理解し、情報検索の授業の内容に基づいて、より良い実践体験を得ました。Luceneは優れたオープンソースの全テキスト検索技術フレームワークであり、その深入研究を通じて、その実現メカニズムをよりよく理解し、その研究の過程で多くの面向オブジェクトのプログラミング方法と思想を学びました。その優れたシステムフレームワークと拡張性は学ぶ価値があります。

声明:この記事の内容はインターネットから取得され、著作権者に帰属します。インターネットユーザーが自発的に提供し、自己でアップロードしたものであり、このサイトは所有権を持ちません。人工編集は行われていません。また、関連する法的責任を負いません。著作権侵害の疑いがある場合は、メールを送信してください:notice#oldtoolbag.com(メールを送信する際には、#を@に変更してください。申し訳ありませんが、関連する証拠を提供し、事実を確認された場合、このサイトは即座に侵害疑いのコンテンツを削除します。)

基礎教程
おすすめ