元テキスト : 文書名 配下に キーワード がたくさん結びつついているデータとみなすことができる ↓ Indexファイル : キーワードに文書名が結びついているデータ ↓ 検索 : キーワードから文書名を探す
<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>LuceneExam</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.github.lucene-gosen</groupId>
<artifactId>lucene-gosen</artifactId>
<version>6.2.1</version>
<classifier>ipadic</classifier>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.mycompany.luceneexam;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.gosen.GosenAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
public class Indexer {
public static void main(String[] args) {
try {
File home = new File(System.getProperty("user.home"));
// インデックスの出力先を定義
Directory indexDir = FSDirectory.open(new File(home, "Documents/index").toPath());
// テキストの解析方法(アナライザー)を定義
//Analyzer analyzer = new StandardAnalyzer(); // 英語用
Analyzer analyzer = new GosenAnalyzer();
// 解析方法の設定
IndexWriterConfig config = new IndexWriterConfig(analyzer);
// インデックスが既に存在する場合の動作を定義する(OpenMode.CREATE の場合、新規に作成して上書きする)
config.setOpenMode(OpenMode.CREATE);
try (IndexWriter writer = new IndexWriter(indexDir, config)) {
File root = new File(home, "Documents/novels");
gatherDocs(writer, root);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void gatherDocs(IndexWriter writer, File parent) throws IOException {
System.out.println(parent.getAbsolutePath());
for (File child : parent.listFiles()) {
if (child.isDirectory()) {
gatherDocs(writer, child);
continue;
}
String name = child.getName();
if (name.startsWith(".")) {
continue;
}
if (name.endsWith("txt")) {
System.out.format("FILE:%s\n", name);
try (BufferedReader br = Files.newBufferedReader(child.toPath(), Charset.forName("Windows-31J"))) {
String title = br.readLine();
String author = br.readLine();
System.out.format("%s %s\n", title, author);
// Document に、インデックスに保存する各ファイルの情報を設定する
Document doc = new Document();
doc.add(new StringField("author", author, Store.YES));
doc.add(new StringField("title", title, Store.YES));
doc.add(new TextField("contents", br));
// インデックスを書き出す
writer.addDocument(doc);
} catch(java.lang.Exception e) {
System.out.println("読み込み失敗");
}
}
}
}
}
package com.mycompany.luceneexam;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.gosen.GosenAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
public class Searcher {
public static void main(String[] args) throws IOException, ParseException {
try {
File home = new File(System.getProperty("user.home"));
// テキストの解析方法(アナライザー)を定義
//Analyzer analyzer = new StandardAnalyzer(); // 英語用
Analyzer analyzer = new GosenAnalyzer();
// 検索対象のフィールドを第二引数で指定している
QueryParser parser = new QueryParser("contents", analyzer);
// 検索文字列を解析する
String searchText = args[0];
Query query = parser.parse(searchText);
// 検索で使用する IndexSearcher を生成する
Directory indexDir = FSDirectory.open(new File(home, "Documents/index").toPath());
IndexReader indexReader = DirectoryReader.open(indexDir);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 検索を実行する(第二引数は、検索結果の最大数)
TopDocs results = indexSearcher.search(query, 10);
// 検索の結果、該当した Document を1つずつ取得する
for (ScoreDoc scoreDoc : results.scoreDocs) {
Document doc = indexSearcher.doc(scoreDoc.doc);
// Document の path を取得して出力する
String author = doc.get("author");
String title = doc.get("title");
System.out.format("%s, %s, %f\n", author, title, scoreDoc.score);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
$ mvn clean compile package dependency:copy-dependencies $ export CLASSPATH=target/LuceneExam-1.0-SNAPSHOT.jar:target/dependency/* $ time java com.mycompany.luceneexam.Indexer ... FILE:219kaneno_oto.txt 錢形平次捕物控 鐘の音 FILE:lavoisier.txt ラヴォアジエ 石原純 FILE:kasojinbutsu.txt 仮装人物 徳田秋声 FILE:zatsudansho.txt 雑談抄 牧野信一 real 2m32.320s user 2m44.847s sys 0m1.516s
Indexファイル作るの相当早いな。10GB処理するのにたかだか2分。色々あって結構良い計算機を衝動買いしちゃったのです
$ time java com.mycompany.luceneexam.Searcher 人生やめたい 久生十蘭, 春雪, 6.566760 MORNING AND EVENING THOUGHTS, 朝に想い、夜に省みる, 6.547380 佐々木邦, 一年の計, 6.488889 坂口安吾, 不可解な失恋に就て, 6.420032 三木清, 如何に読書すべきか, 6.196942 岸田國士, 新劇協会の舞台稽古, 6.195092 THE YELLOW FACE, 土色の顔, 6.138238 三好十郎, 恐怖の季節, 6.082151 和辻哲郎, 露伴先生の思い出, 6.064951 辻潤, ふもれすく, 6.057560 real 0m0.368s user 0m0.644s sys 0m0.051s
検索、早!それっぽい題名の小説が検索された
$ time java com.mycompany.luceneexam.Searcher 夜明け前が一番暗い 中谷宇吉郎, 八月三日の夢, 9.644770 クリスマス・ストーリー, 千里眼の村, 9.404030 川端茅舎, 夏の月, 8.961294 THE OLD MAN AND THE SEA, 老人と海, 8.874863 織田作之助, 秋の暈, 8.737694 島崎藤村, 秋草, 8.694632 原民喜, 魔のひととき, 8.631346 坂口安吾, 桐生通信, 8.517919 中谷宇吉郎, I駅の一夜, 8.474227 室生犀星, 寂しき魚, 8.410800 real 0m0.374s user 0m0.655s sys 0m0.058s
ほーほー、ずっと遊べるな