Lucene全文檢索之倒排索引實現原理、API解析【2018.11】
》
官網 http://lucene.apache.org/
下載地址:https://mirrors.tuna.tsinghua.edu.cn/apache/lucene/java/7.5.0/
》
Lucene的全文檢索是指什麼:
程式掃描文件,對文件document建立索引,並對文件進行分詞 ik,對每一個詞建立索引並關聯文件document的編號(索引);
當用戶進行搜尋時候,按照搜尋條件的分詞的索引,匹配出對應的文件索引,把具體的文件展現出來,整個過程就是全文索引(倒排索引基本原理);
->
->全文檢索的特點:
1、只處理文字不處理語義
2、搜尋時英文不區分大小寫
3、結果列表有關度 排行
》
1、全文檢索和資料庫模糊查詢(或者模糊匹配)的區別:
1、資料庫模糊匹配會查詢出許多和期望無關的資料;
=>假設搜尋it相關的:
=>select * from db where name like ‘%’ it ‘%’ 會搜尋出git ,顯然結果並不是我們所期望的;
2、相關度排序問題資料庫無法解決;
3、資料庫模糊匹配效率較低;
》
2、Lucene與Solr的關係
/
->Lucene: 是全文檢索的底層API;
->Solr: 基於Lucene開發的搜尋伺服器;
》
3、使用Lucene需要的依賴:
<dependencies>
<!-- Junit單元測試 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- lucene核心庫 -->
< dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.10.2</version>
</dependency>
<!-- Lucene的查詢解析器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>4.10.2</version>
</dependency>
<!-- lucene的預設分詞器庫 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.10.2</version>
</dependency>
<!-- lucene的高亮顯示 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>4.10.2</version>
</dependency>
<!--ik分詞器 -->
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</dependency>
</dependencies>
》>1、建立測試類完成一次分詞:
1、建立文件,新增 StringField、TextField
2、對文件分詞:使用 IKAnalyzer 分詞器
- FSDirectory建立目錄
- DirectoryReader 讀取Directory
- 搜尋工具IndexSearcher
3、建立寫的工具:
- 配置IndexWriterConfig寫的配置,設定規則
- 生成寫的物件IndexWriter
4、提交關閉
- commit
- close
》
以下測試原始碼分享地址:
https://github.com/medoo-Ai/lucene
/**
* @auther SyntacticSugar
* @data 2018/11/23 0023上午 11:37
*/
public class CreateIndexTest {
/**
* 給 文件新增fieldName /FieldText/store 是yes or no 控制是否儲存
*/
@Test
public void createIndexTest() {
Document document = new Document();
document.add(new StringField("id", "1", Field.Store.YES));
document.add(new TextField("title", "谷歌地圖之父跳槽facebook厲害了我的哥碉堡了" +
"藍瘦香菇", Field.Store.YES));
// 建立儲存目錄、分詞
try {
FSDirectory indexDir = FSDirectory.open(new File("indexDir"));
// StandardAnalyzer analyzer = new StandardAnalyzer();
IKAnalyzer analyzer = new IKAnalyzer();
/**
* 建立文件配置物件,IndexWriterConfig.OpenMode.CREATE 清空建立索引
* IndexWriterConfig.OpenMode.APPEND 在原來基礎上追加
*/
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST, analyzer);
indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
//索引
IndexWriter indexWriter = new IndexWriter(indexDir, indexWriterConfig);
//
//寫入索引,提交,關閉
indexWriter.addDocument(document);
indexWriter.commit();
indexWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
》生成如下indexDir目錄,目錄內是分詞;
》2、建新測試,新增多個document
文件document中分詞,可以新增集合list
並且 ,建立文件配置物件 indexWriterConfig 時候:
- IndexWriterConfig.OpenMode.CREATE 清空建立索引
- IndexWriterConfig.OpenMode.APPEND 在原來基礎上追加
/**
* 給 文件新增filedName /FiledText/store 是yes or no
*/
@Test
public void createIndexTest2() throws IOException {
ArrayList<Document> documents = new ArrayList<>();
Document document1 = new Document();
document1.add(new StringField("id", "1", Field.Store.YES));
document1.add(new TextField("title", "谷歌地圖之父跳槽facebook厲害了我的哥碉堡了" +
"藍瘦香菇", Field.Store.YES));
Document document2 = new Document();
document2.add(new StringField("id", "2", Field.Store.YES));
document2.add(new TextField("title", "谷歌地圖之父拉斯加盟社交網站Facebook" +
"藍瘦香菇", Field.Store.YES));
//新增到集合中
documents.add(document1);
documents.add(document2);
// 建立儲存目錄、分詞
FSDirectory indexDir = FSDirectory.open(new File("indexDir"));
// StandardAnalyzer analyzer = new StandardAnalyzer();
IKAnalyzer analyzer = new IKAnalyzer();
/**
* 建立文件配置物件,IndexWriterConfig.OpenMode.CREATE 清空建立索引
* IndexWriterConfig.OpenMode.APPEND 在原來基礎上追加
*/
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST, analyzer);
indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
//索引
IndexWriter indexWriter = new IndexWriter(indexDir, indexWriterConfig);
//
//寫入索引,提交,關閉s
indexWriter.addDocuments(documents);
indexWriter.commit();
indexWriter.close();
}
》3、新建測試類,使用分詞查詢 “谷歌地圖之父” ,為多條件查詢
3、Query查詢:
步驟如下:
1、建立目錄FSDirectory 、建立索引讀取工具DirectoryReader、建立索引搜尋工具indexSearcher
2、查詢解析器:QueryParser parser
- 解析要查詢的 parse(String query)生成query物件
3、使用搜索工具indexSearcher進行查詢,把query物件傳參進去,返回topDocs文件物件
- topDocs包含totalHits查詢的總數目、scoreDocs分數
- 對scoreDocs 進行遍歷,獲取每一個文件的 編號docId 、得分
- 通過搜尋工具 indexSearcher對文件docId 編號搜尋,找到具體的文件物件 document
4、document文件物件中,包含id / title 兩部分,get取出
- String id = doc.get(“id”);
- String title = doc.get(“title”);
對於查詢解析器 parse,原始碼如下:
@Test
public void testSearch() throws IOException, ParseException {
FSDirectory indexDir = FSDirectory.open(new File("indexDir"));
/**
* 索引讀取工具
* 索引搜尋工具
*/
IndexReader open = DirectoryReader.open(indexDir);
IndexSearcher indexSearcher = new IndexSearcher(open);
//建立查詢解析器[可以傳參入一個數組,多條件進行查詢],建立要查詢的物件
// QueryParser parser = new QueryParser("title", new IKAnalyzer());
QueryParser parser = new MultiFieldQueryParser(new String[]{"id", "title"}, new IKAnalyzer());
Query query = parser.parse("谷歌地圖之父");
//搜尋
//topDocs包含totalHits、scoreDocs
TopDocs topDocs = indexSearcher.search(query, 5);//引數5,就是排名前五名的
System.out.println("查詢到資料的條數" + topDocs.totalHits);
/**
* 獲取文件編號中文件的編號以及文件的得分
*/
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;
float score = scoreDoc.score;
//根據文件編號查詢 doc
Document doc = indexSearcher.doc(docId);
String id = doc.get("id");
String title = doc.get("title");
System.out.println(id + ":" + title);
}
}
》抽取search查詢的工具類;
》
經常要用:
抽取了一個query查詢的工具類:
public void search(Query query) throws IOException {
//建新目錄,讀取工具,搜尋工具
FSDirectory directory = FSDirectory.open(new File("indexDir"));
DirectoryReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
/**
* 搜尋符合條件的topDocs
* 對文件進行解析 獲取 totalHits ,scoreDocs
*/
TopDocs topDocs = searcher.search(query, 10);
System.out.println("本次搜尋到的條數:" + topDocs.totalHits);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
//遍歷
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;
float score = scoreDoc.score;
//查詢到對應的文件
Document doc = searcher.doc(docId);
System.out.println(doc.get("id") + ":" + doc.get("title") + ":" + score);
}
}
》詞條查詢:
/**
* testTermQuery 詞條查詢
*/
@Test
public void testTermQuery() throws IOException {
// 建立詞條查詢的物件
TermQuery title = new TermQuery(new Term("title", "谷歌"));
//呼叫查詢方法進行查詢
search(title);
}
/**
* WildcardQuery 萬用字元 ? 查詢
*/
@Test
public void WildcardQuery() throws IOException {
TermQuery query = new TermQuery(new Term("title", "??"));
search(query);
}
》萬用字元查詢:
testFuzzyQuery 萬用字元查詢、 Fuzzy:英文有 :失真、模糊 的意思
- 詞條中並沒有 “谷歌啊” 這樣的詞條,卻查出來資訊,因為模糊設定了編輯距離(容錯);
- 編輯距離 0-2
/**
* testFuzzyQuery 萬用字元查詢 Fuzzy:失真、模糊
* fscevool 編輯距離0-2
*/
@Test
public void testFuzzyQuery() throws IOException {
FuzzyQuery query = new FuzzyQuery(new Term("title", "谷歌啊"), 1);
search(query);
}
》組合查詢:
1、NumericRangeQuery 數字查詢範圍:
- 可以進行精確的查詢
- 引數:欄位名稱,最小值、最大值、是否包含最小值、是否包含最大值
2、 BooleanQuery(組合查詢)
建立組合查詢物件query,把查詢物件query1,query2分別新增到query中
@Test
public void testNumericRangeQuery() throws IOException {
//Query介面
Query query = NumericRangeQuery.newLongRange("id", 2L, 2L, true, true);
search(query);
}
/**
* BooleanQuery(組合查詢)
* Occur.MUST_NOT 不是必須的
* Occur.SHOULD 必須的
*/
@Test
public void testBooleanQuery() throws IOException {
Query query1 = NumericRangeQuery.newLongRange("id", 2L, 2L, true, true);
Query query2 = NumericRangeQuery.newLongRange("id", 2L, 4L, true, true);
//建立boolean查詢,然後新增query1/query2
BooleanQuery query = new BooleanQuery();
query.add(query1, BooleanClause.Occur.MUST_NOT);
query.add(query2, BooleanClause.Occur.SHOULD);
search(query);
}
》修改,刪除
/**
* 修改索引
* 先刪除,後建立
* 1、所以我們是修改 一把針對唯一的進行修改
* 2、deleteDocuments 若id 是數字,直接刪除即可
*/
@Test
public void testUpdate() throws IOException {
//建立目錄,建立IndexWriterConfig,建立索引寫出物件
FSDirectory directory = FSDirectory.open(new File("indexDir"));
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());
IndexWriter indexWriter = new IndexWriter(directory, config);
//建立文件索引資料,而後進行修改
Document document = new Document();
document.add(new StringField("id", "1", Field.Store.YES));
document.add(new TextField("title", "谷歌地圖之父跳槽facebook 為了加入復仇者聯盟 屌爆了啊", Field.Store.YES));
/**
* 修改
* Term 詞條
* document 文件
*/
indexWriter.updateDocument(new Term("id", "1"), document);
indexWriter.commit();
indexWriter.close();
}
/**
* testDelete
* 刪除詞條 ,通過id 來刪除 indexWriter.deleteDocuments(new Term("id", "1"));
* 1、刪除所有 deleteAll
* 2、條件刪除 query來刪除
*/
@Test
public void testDelete() throws IOException {
//建立目錄,建立IndexWriterConfig,建立索引寫出物件
FSDirectory directory = FSDirectory.open(new File("indexDir"));
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());
IndexWriter indexWriter = new IndexWriter(directory, config);
//建立文件索引資料
// indexWriter.deleteDocuments(new Term("id", "1"));
Query query = NumericRangeQuery.newLongRange("id", 2L, 2L, true, true);
indexWriter.deleteDocuments(query);
indexWriter.commit();
indexWriter.close();
}
》程式碼高亮顯示:
完成程式碼高亮顯示;
主要使用了格式化器和高亮工具:
- 程式碼高亮工具構造器:public Highlighter(Formatter formatter, Scorer fragmentScorer) , 其中fragmentScorer 是切面評分,也就是queryScorer,對顯示的內容進行一個評分(因為要顯示的東西太多了,不能全部都高亮的吧,通過queryScorer來確定);
- 格式化器SimpleHTMLFormatter 作為引數傳遞,指定了格式,本例中採用斜體 ;
@Test
public void testHighlighter() throws Exception {
// 目錄,directoryreader ,searcher
FSDirectory directory = FSDirectory.open(new File("indexDir"));
DirectoryReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
//分詞
QueryParser parser = new QueryParser("title", new IKAnalyzer());
Query query = parser.parse("谷歌地圖");
searcher.search(query, 10);
/**
* 格式化器:
* 準備高亮工具:
*/
SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<em>", "</em>");
QueryScorer queryScorer = new QueryScorer(query);
Highlighter highlighter = new Highlighter(formatter, queryScorer);
//搜尋文件資料
TopDocs topDocs = searcher.search(query, 10);
System.out.println("本次搜尋到的條數:" + topDocs.totalHits);
//bianli
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
// 獲取文件編號,得分編號docId / score
int docId = scoreDoc.doc;
float score = scoreDoc.score;
//查詢文件的 title 、得分
Document doc = searcher.doc(docId);
String title = doc.get("title");
// 高亮處理 Analyzer analyzer, String fieldName, String text
String bestFragment = highlighter.getBestFragment(new IKAnalyzer(), "title", title)
相關推薦
Lucene全文檢索之倒排索引實現原理、API解析【2018.11】
》
官網 http://lucene.apache.org/ 下載地址:https://mirrors.tuna.tsinghua.edu.cn/apache/lucene/java/7.5.0/
》
Lucene的全文檢索是指什麼:
程式掃描文件
Hadoop學習之網路爬蟲+分詞+倒排索引實現搜尋引擎案例
本專案實現的是:自己寫一個網路爬蟲,對搜狐(或者csdn)爬取新聞(部落格)標題,然後把這些新聞標題和它的連結地址上傳到hdfs多個檔案上,一個檔案對應一個標題和連結地址,然後通過分詞技術對每個檔案中的標題進行分詞,分詞後建立倒排索引以此來實現搜尋引擎的功能,建
Elastic 之倒排索引(二)
mage bsp post elastic 分享圖片 位置 png blog 通過 常規索引建立:
文檔--》關鍵詞的映射過程(正向索引)
缺點:費時 便利全部文檔
倒排反向建立索引:
關鍵詞--》文檔的映射
反向到倒排索引:將索引的關鍵詞出現的文檔的位置和出現頻率
大數據MapReduce入門之倒排索引
tsp 功能 nbsp bstr 生成 path 需要 turn 們的 在上一篇博客中我們講解了MapReduce的原理以及map和reduce的作用,相信你理解了他們的原理,今天講解的是mapreduce 的另一個就是倒排索引。
什麽是倒排索引呢?倒排索
一些演算法的MapReduce實現——倒排索引實現
/**
* input format
* docid<tab>doc content
*
* output format
* (term:docid)<tab>(tf in this doc)
*
*/
public s
Hadoop 文件倒排索引實現
在上黃宜華老師的MapReduce的課程中,會有實驗讓實現帶詞頻的文件倒排索引。一般情況下根據他的書就能實現基本的東西,但是根據書上的程式碼,執行的時候可能會有一些小的trick,會報出一些異常。其實如果參照這個文章 《Hadoop之倒排索引》就能實現所需要的功能了。但是本
mapreduce演算法之倒排索引
package mapreduce;
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.
mapreduce典型應用案例之倒排索引
一、倒排索引的介紹
通俗的講,就是根據單詞找到包含這個單詞的所有文件。
二、mapreduce實現框架
1、首先要確定map、reduce、combiner中的key和value是什麼型別 2、然後確定key和value具體是什麼? Map : key為 單詞+檔名 value為
大資料開發之倒排索引詳解,拭目以待
在現如今,隨著網際網路技術和大資料+人工智慧的飛速發展,越來越多人想要學習大資料開發,那麼今天大資料培訓之倒排索引的詳細介紹,下面我們一起來看一下吧。
首先大資料在經濟、政治、文化等方面有著深遠的影響,大資料可以幫助人們開啟循“數”管理的模式,也是我們當下“大社會”的集中體現,大資料高階班的課程
2 Elasticsearch 篇之倒排索引與分詞
文章目錄
書的目錄與索引
正排與倒排索引簡介
倒排索引詳解
分詞介紹
analyze_api
自帶分詞器
Standard Analyzer
Simple Analyzer
W
MapReduce 案例之倒排索引
MapReduce 案例之倒排索引 1. 倒排索引 倒排索引是文件檢索系統中最常用的資料結構,被廣泛地應用於全文搜尋引擎。 它主要是用來儲存某個單詞(或片語) 在一個文件或一組文件中的儲存位置的對映,即提供了一種根據內容來查詢文件的方式。由於不是根據文件來確定文件所包含的內容,而是進行相反的操作,因
MapReduce程式設計之倒排索引
任務要求:
//輸入檔案格式
18661629496 110
13107702446 110
1234567 120
2345678 120
987654 110
2897839274 18661629496
//輸出檔
【Hadoop基礎教程】9、Hadoop之倒排索引
開發環境
硬體環境:Centos 6.5 伺服器4臺(一臺為Master節點,三臺為Slave節點)
軟體環境:Java 1.7.0_45、hadoop-1.2.1
1、倒排索引
倒排索引是文件檢索系統中最常用的資料結構,被廣泛用於全文搜尋引
40.倒排索引核心原理
提升 兩個 知識點 初步 blog 微軟雅黑 spa word ttl 主要知識點
倒排索引核心原理:normalization
假設有兩個文檔,內容如下
doc1:I really liked my small dogs, and I think my mom
ElasticSearch最佳入門實踐(三十九)倒排索引核心原理揭祕
1、例子,兩段文字
doc1:I really liked my small dogs, and I think my mom also liked them
doc2:He never liked any dogs, so I hope that my m
倒排索引查詢原理
Lucene 查詢過程
在lucene中查詢是基於segment。每個segment可以看做是一個獨立的subindex,在建立索引的過程中,lucene會不斷的flush記憶體中的資料持久化形成新的segment。多個segment也會不斷的被merge成一個大的segm
Lucene倒排索引簡述 之倒排表
一、前言
上一篇《Lucene倒排索引簡述 之索引表》,已經對整個倒索引的結構進行大體介紹,並且詳細介紹了索引表(TermsDictionary)的內容。同時還詳細介紹了Lucene關於索引表的實現,相關檔案結構詳解,以及對索引表採用的資料結構進行剖析解讀。
Lucene倒排索引簡述 之索引表
一、前言
倒排索引是全文檢索的根基,理解了倒排索引之後才能算是入門了全文檢索領域。倒排索引的的概念很簡單,也很好理解。但如你知道在全文檢索領域Lucene可謂是獨領風騷。所以你真的瞭解Lucene的倒排了嗎?Lucene是如何實現這個結構的呢?
倒排索引如此重
Lucene 4.X 倒排索引原理與實現: (1) 詞典的設計
詞典的格式設計
詞典中所儲存的資訊主要是三部分:
Term字串
Term的統計資訊,比如文件頻率(Document Frequency)
倒排表的位置資訊
其中Term字串如何儲存是一個很大的問題,根據上一章基本原理的表述中,我們知道,寫入檔案的Term是按照字典順序排好序的,那麼如何將這些
Lucene倒排索引原理與實現:Term Dictionary和Index檔案 (FST詳細解析)
我們來看最複雜的部分,就是Term Dictionary和Term Index檔案,Term Dictionary檔案的字尾名為tim,Term Index檔案的字尾名是tip,格式如圖所示。
Term Dictionary檔案首先是一個Header,接下來是Pos