日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(转)Lucene

發(fā)布時間:2025/5/22 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (转)Lucene 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

lucene

Lucene.Net?系列一本文介紹了什么是Lucene,Lucene能做什么.

如何從一個文件夾下的所有txt文件中查找特定的詞?

本文將圍繞該個實例介紹了lucene.net的索引的建立以及如何針對索引進行搜索.最后還將給出源代碼供大家學習.

源代碼下載

What’s Lucene
Lucene是一個信息檢索的函數(shù)庫(Library),利用它你可以為你的應用加上索引和搜索的功能.

Lucene的使用者不需要深入了解有關全文檢索的知識,僅僅學會使用庫中的一個類,你就為你的應用實現(xiàn)全文檢索的功能.

不過千萬別以為Lucene是一個象google那樣的搜索引擎,Lucene甚至不是一個應用程序,它僅僅是一個工具,一個Library.你也可以把它理解為一個將索引,搜索功能封裝的很好的一套簡單易用的API.利用這套API你可以做很多有關搜索的事情,而且很方便.

What Can Lucene Do

Lucene可以對任何的數(shù)據(jù)做索引和搜索. Lucene不管數(shù)據(jù)源是什么格式,只要它能被轉化為文字的形式,就可以被Lucene所分析利用.也就是說不管是MS word, Html ,pdf還是其他什么形式的文件只要你可以從中抽取出文字形式的內容就可以被Lucene所用.你就可以用Lucene對它們進行索引以及搜索.

How To Use Lucene --- A Simple Example
示例介紹:

為作為輸入?yún)?shù)的文件夾下的所有txt類型的文件做索引,做好的索引文件放入index文件夾.

然后在索引的基礎上對文件進行全文搜索.

1.???????建立索引
IndexWriter writer = new IndexWriter("index", new StandardAnalyzer(), true);
IndexDocs(writer, new System.IO.FileInfo(args[0]));???????????????
writer.Optimize();
writer.Close();

IndexWriter是對索引進行寫操作的一個類,利用它可以創(chuàng)建一個索引對象然后往其中添加文件.需要注意它并不是唯一可以修改索引的類.在索引建好后利用其他類還可以對其進行修改.

構造函數(shù)第一個參數(shù)是建立的索引所要放的文件夾的名字.第二個參數(shù)是一個分析對象,主要用于從文本中抽取那些需要建立索引的內容,把不需要參與建索引的文本內容去掉.比如去掉一些a the之類的常用詞,還有決定是否大小寫敏感.不同的選項通過指定不同的分析對象控制.第三個參數(shù)用于確定是否覆蓋原有索引的.

第二步就是利用這個writer往索引中添加文件.具體后面再說.

第三步進行優(yōu)化.

第四步關閉writer.

?

下面具體看看第二步:

???public static void IndexDirectory(IndexWriter writer, FileInfo file)
???????? {
????????????? if (Directory.Exists(file.FullName))
????????????? {
?????????????????? String[] files = Directory.GetFileSystemEntries(file.FullName);
?????????????????? // an IO error could occur
?????????????????? if (files != null)
?????????????????? {
?????????????????????? for (int i = 0; i < files.Length; i++)
?????????????????????? {
??????????????????????????? IndexDirectory(writer, new FileInfo(files[i]));? //這里是一個遞歸
?????????????????????? }
?????????????????? }
????????????? }
????????????? else if (file.Extension == ".txt")
????????????? {
?????????????????? IndexFile(file, writer);
????????????? }
???????? }

?

???????? private static void IndexFile(FileInfo file, IndexWriter writer)
???????? {
????????????? Console.Out.WriteLine("adding " + file);
????????????? try
????????????? {
?????????????????? Document doc = new Document();???????????????????
?????????????????? doc.Add(Field.Keyword("filename", file.FullName));

?????????????????? doc.Add(Field.Text("contents", new StreamReader(file.FullName)));

?????????????????? writer.AddDocument(doc);
????????????? }
??????????????
????????????? catch (FileNotFoundException fnfe)
????????????? {
???????????????????
????????????? }
???? }

主要就是兩個函數(shù)一個用于處理文件夾(不是為文件夾建立索引),一個用于真正為文件建立索引.

因此主要集中看一下IndexFile這個方法.首先建立Document對象,然后為Document對象添加一些屬性Field.你可以把Document對象看成是虛擬文件,將來將從此獲取信息.而Field則看成是描述此虛擬文件的元數(shù)據(jù)(metadata).

其中Field包括四個類型:

Keywork

該類型的數(shù)據(jù)將不被分析,而會被索引并保存保存在索引中.

UnIndexed

該類型的數(shù)據(jù)不會被分析也不會被索引,但是會保存在索引.

UnStored

和UnIndexed剛好相反,被分析被索引,但是不被保存.

Text

和UnStrored類似.如果值的類型為string還會被保存.如果值的類型Reader就不會被保存和UnStored一樣.

?

最后將每一個Document添加到索引當中.

需要注意的是索引不僅可以建立在文件系統(tǒng)上,也可以建立在內存中.

例如

IndexWriter writer = new IndexWriter("index", new StandardAnalyzer(), true);

在第一個參數(shù)不是指定文件夾的名字而是使用Directory對象,并使用它的子類RAMDirectory,就可以將索引建立在內存當中.

?

2.???????對索引進行搜索

IndexSearcher indexSearcher= new IndexSearcher(indexDir);
Query query = QueryParser.Parse(queryString, "contents",new StandardAnalyzer());
Hits hits = indexSearcher.Search(query);

?第一步利用IndexSearcher打開索引文件用于后面搜索,其中的參數(shù)是索引文件的路徑.

第二步使用QueryParser將可讀性較好的查詢語句(比如查詢的詞lucene ,以及一些高級方式lucene AND .net)轉化為Lucene內部使用的查詢對象.

第三步執(zhí)行搜索.并將結果返回到hits集合.需要注意的是Lucene并不是一次將所有的結果放入hits中而是采取一次放一部分的方式.出于空間考慮.

作者 idior ??

2005-03-16 22:36

?

Lucene.net?系列二?--- index?一詳細介紹了有關Lucene.net索引添加刪除更新的詳細內容.并給出了所有的TestCase供學習參考.

Lucene建立Index的過程:

1.???????抽取文本.

?? 比如將PDF以及Word中的內容以純文本的形式提取出來.Lucene所支持的類型主要為String,為了方便同時也支持Date 以及Reader.其實如果使用這兩個類型lucene會自動進行類型轉換.

2.???????文本分析.

?? Lucene將針對所給的文本進行一些最基本的分析,并從中去除一些不必要的信息,比如一些常用字a ,an, the 等等,如果搜索的時候不在乎字母的大小寫, 又可以去掉一些不必要的信息.總而言之你可以把這個過程想象成一個文本的過濾器,所有的文本內容通過分析, 將過濾掉一些內容,剩下最有用的信息.

3.???????寫入index.

和google等常用的索引技術一樣lucene在寫index的時候都是采用的倒排索引技術(inverted index.) 簡而言之,就是通過某種方法(類似hash表?)將常見的”一篇文檔中含有哪些詞”這個問題轉成”哪篇文檔中有這些詞”. 而各個搜索引擎的索引機制的不同主要在于如何為這張倒排表添加更準確的描述.比如google有名的PageRank因素.Lucene當然也有自己的技術,希望在以后的文章中能為大家加以介紹.

在上一篇文章中,使用了最基本的建立索引的方法.在這里將對某些問題加以詳細的討論.

1. 添加Document至索引
上次添加的每份文檔的信息是一樣的,都是文檔的filename和contents.

doc.Add(Field.Keyword("filename", file.FullName));
doc.Add(Field.Text("contents", new StreamReader(file.FullName)));

在Lucene中對每個文檔的描述是可以不同的,比如,兩份文檔都是描述一個人,其中一個添加的是name, age 另一個添加的是id, sex ,這種不規(guī)則的文檔描述在Lucene中是允許的.
還有一點Lucene支持對Field進行Append , 如下:

string baseWord = "fast";
string synonyms[] = String {"quick", "rapid", "speedy"};
Document doc = new Document();
doc.Add(Field.Text("word", baseWord));
for (int i = 0; i < synonyms.length; i++)?
??? doc.Add(Field.Text("word", synonyms[i]));

這點純粹是為了方便用戶的使用.在內部Lucene自動做了轉化,效果和將它們拼接好再存是一樣.

2.?刪除索引中的文檔

??? 這一點Lucene所采取的方式比較怪,它使用IndexReader來對要刪除的項進行標記,然后在Reader Close的時候一起刪除.
這里簡要介紹幾個方法.

[TestFixture]
public class DocumentDeleteTest : BaseIndexingTestCase?? // BaseIndexingTestCase
中的SetUp方法?????????? ??????????????????????????????????? //建立了索引其中加入了兩個Document
{
??? [Test]
??? public void testDeleteBeforeIndexMerge()
??? {
????
??????? IndexReader reader = IndexReader.Open(dir);? //
當前索引中有兩個Document

??????? Assert.AreEqual(2, reader.MaxDoc());?? //文檔從0開始計數(shù),MaxDoc表示下一個文檔的序號

??????? Assert.AreEqual(2, reader.NumDocs());? //NumDocs表示當前索引中文檔的個數(shù)??
??????? reader.Delete(1);?????????????????? //
對標號為1的文檔標記為待刪除,邏輯刪除
??????? Assert.IsTrue(reader.IsDeleted(1));???????? //檢測某個序號的文檔是否被標記刪除
??????? Assert.IsTrue(reader.HasDeletions());?????? //檢測索引中是否有Document被標記刪除?
??????? Assert.AreEqual(2, reader.MaxDoc());??????? //
當前下一個文檔序號仍然為2
??????? Assert.AreEqual(1, reader.NumDocs());?????? //
當前索引中文檔數(shù)變成1
??????? reader.Close();???????????????????????????? //
此時真正從物理上刪除之前被標記的文檔
??????? reader = IndexReader.Open(dir);
??????? Assert.AreEqual(2, reader.MaxDoc());?????????
??????? Assert.AreEqual(1, reader.NumDocs());
??????? reader.Close();
??? }

??? [Test]
??? public void DeleteAfterIndexMerge()????//
在索引重排之后
??? {
??????? IndexReader reader = IndexReader.Open(dir);
??????? Assert.AreEqual(2, reader.MaxDoc());
??????? Assert.AreEqual(2, reader.NumDocs());
??????? reader.Delete(1);
??????? reader.Close();
??????? IndexWriter writer = new IndexWriter(dir, GetAnalyzer(), false);
??????? writer.Optimize();?????????????????//
索引重排
??????? writer.Close();
??????? reader = IndexReader.Open(dir);
??????? Assert.IsFalse(reader.IsDeleted(1));
??????? Assert.IsFalse(reader.HasDeletions());
??????? Assert.AreEqual(1, reader.MaxDoc());?????? //
索引重排后,下一個文檔序號變?yōu)?/strong>1
??????? Assert.AreEqual(1, reader.NumDocs());
??????? reader.Close();
??? }
}


當然你也可以不通過文檔序號進行刪除工作.采用下面的方法,可以從索引中刪除包含特定的內容文檔.

IndexReader reader = IndexReader.Open(dir);
reader.Delete(new Term("city", "Amsterdam"));
reader.Close();

你還可以通過reader.UndeleteAll()這個方法取消前面所做的標記,即在read.Close()調用之前取消所有的刪除工作

3.?更新索引中的文檔

?? 這個功能Lucene沒有支持, 只有通過刪除后在添加來實現(xiàn). 看看代碼,很好理解的.

[TestFixture]
public class DocumentUpdateTest : BaseIndexingTestCase
{
??? [Test]
??? public void Update()
??? {
??????? Assert.AreEqual(1, GetHitCount("city", "Amsterdam"));
??????? IndexReader reader = IndexReader.Open(dir);
??????? reader.Delete(new Term("city", "Amsterdam"));
??????? reader.Close();
??????? Assert.AreEqual(0, GetHitCount("city", "Amsterdam"));
??????? IndexWriter writer = new IndexWriter(dir, GetAnalyzer(),false);
??????? Document doc = new Document();
??????? doc.Add(Field.Keyword("id", "1"));

?

??????? doc.Add(Field.UnIndexed("country", "Netherlands"));
??????? doc.Add(Field.UnStored("contents","Amsterdam has lots of bridges"));
??????? doc.Add(Field.Text("city", "Haag"));
??????? writer.AddDocument(doc);
??????? writer.Optimize();
??????? writer.Close();
??????? Assert.AreEqual(1, GetHitCount("city", "Haag"));
??? }

?

??? protected override Analyzer GetAnalyzer()
??? {
??????? return new WhitespaceAnalyzer();? //
注意此處如果用SimpleAnalyzer搜索會失敗,因為建立索引的時候使用的SimpleAnalyse它會將所有字母變成小寫.

??? }

??? private int GetHitCount(String fieldName, String searchString)
??? {
??????? IndexSearcher searcher = new IndexSearcher(dir);
??????? Term t = new Term(fieldName, searchString);
??????? Query query = new TermQuery(t);
??????? Hits hits = searcher.Search(query);
??????? int hitCount = hits.Length();
??????? searcher.Close();
??????? return hitCount;
??? }
}

??? 需要注意的是以上所有有關索引的操作,為了避免頻繁的打開和關閉Writer和Reader.又由于添加和刪除是不同的連接(Writer, Reader)做的.所以應該盡可能的將添加文檔的操作放在一起批量執(zhí)行,然后將刪除文檔的操作也放在一起批量執(zhí)行.避免添加刪除交替進行.

Lucene.net?系列三?--- index?本文將進一步討論有關Lucene.net建立索引的問題:

主要包含以下主題:
1.索引的權重
2.利用IndexWriter 屬性對建立索引進行高級管理
3.利用RAMDirectory充分發(fā)揮內存的優(yōu)勢
4.利用RAMDirectory并行建立索引
5.控制索引內容的長度
6.Optimize 優(yōu)化的是什么?

源代碼下載

本文將進一步討論有關Lucene.net建立索引的問題:

索引的權重
根據(jù)文檔的重要性的不同,顯然對于某些文檔你希望提高權重以便將來搜索的時候,更符合你想要的結果. 下面的代碼演示了如何提高符合某些條件的文檔的權重.

比如對公司內很多的郵件做了索引,你當然希望主要查看和公司有關的郵件,而不是員工的個人郵件.這點根據(jù)郵件的地址就可以做出判斷比如包含@alphatom.com的就是公司郵件,而@gmail.com等等就是私人郵件.如何提高相應郵件的權重? 代碼如下:

???? public static? String COMPANY_DOMAIN = "alphatom.com";
???? Document doc = new Document();
???? String senderEmail = GetSenderEmail();
???? String senderName = getSenderName();
???? String subject = GetSubject();
???? String body = GetBody();
???? doc.Add(Field.Keyword("senderEmail”, senderEmail));
???? doc.Add(Field.Text("senderName", senderName));
???? doc.Add(Field.Text("subject", subject));
???? doc.Add(Field.UnStored("body", body));

???? if (GetSenderDomain().EndsWith(COMPANY_DOMAIN))?

????//如果是公司郵件,提高權重,默認權重是1.0
???? ????? doc.SetBoost(1.5);???????????????????????
???? else?????????????????????????//如果是私人郵件,降低權重.
???? ????? doc.SetBoost(0.1);

???? writer.AddDocument(doc);

不僅如此你還可以對Field也設置權重.比如你對郵件的主題更感興趣.就可以提高它的權重.???

????Field senderNameField = Field.Text("senderName", senderName);

???? Field subjectField = Field.Text("subject", subject);
???? subjectField.SetBoost(1.2);
lucene搜索的時候會對符合條件的文檔按匹配的程度打分,這點就和google的PageRank有點類似, 而SetBoost中的Boost就是其中的一個因素,當然還有其他的因素.這要放到搜索里再說.

利用IndexWriter?變量對建立索引進行高級管理
在建立索引的時候對性能影響最大的地方就是在將索引寫入文件的時候, 所以在具體應用的時候就需要對此加以控制.

在建立索引的時候對性能影響最大的地方就是在將索引寫入文件的時候所以在具體應用的時候就需要對此加以控制

IndexWriter屬性??

默認值

描述

MergeFactory

10

控制segment合并的頻率和大小

MaxMergeDocs

Int32.MaxValue

限制每個segment中包含的文檔數(shù)

MinMergeDocs

10

當內存中的文檔達到多少的時候再寫入segment

?

Lucene默認情況是每加入10份文檔就從內存往index文件寫入并生成一個segement,然后每10個segment就合并成一個segment.通過MergeFactory這個變量就可以對此進行控制.

MaxMergeDocs用于控制一個segment文件中最多包含的Document數(shù).比如限制為100的話,即使當前有10個segment也不會合并,因為合并后的segmnet將包含1000個文檔,超過了限制.

MinMergeDocs用于確定一個當內存中文檔達到多少的時候才寫入文件,該項對segment的數(shù)量和大小不會有什么影響,它僅僅影響內存的使用,進一步影響寫索引的效率.

為了生動的體現(xiàn)這些變量對性能的影響,用一個小程序對此做了說明.

這里有點不可思議.Lucene in Action書上的結果比我用dotLucene做的結果快了近千倍.這里給出書中用Lucene的數(shù)據(jù),希望大家比較一下看看是不是我的問題.

Lucene in Action書中的數(shù)據(jù):

% java lia.indexing.IndexTuningDemo 100000 10 9999999 10
Merge factor: 10
Max merge docs: 9999999
Min merge docs: 10
Time: 74136 ms
% java lia.indexing.IndexTuningDemo 100000 100 9999999 10
Merge factor: 100
Max merge docs: 9999999
Min merge docs: 10
Time: 68307 ms
我的數(shù)據(jù): 336684128 ms
可以看出MinMergeDocs(主要用于控制內存)和MergeFactory(控制合并的次數(shù)和合并后的大小) 對建立索引有顯著的影響.但是并不是MergeFactory越大越好,因為如果一個segment的文檔數(shù)很多的話,在搜索的時候必然也會影響效率,所以這里MergeFactory的取值是一個需要平衡的問題.而MinMergeDocs主要受限于內存.

利用RAMDirectory充分發(fā)揮內存的優(yōu)勢

從上面來看充分利用內存的空間,減少讀寫文件(寫入index)的次數(shù)是優(yōu)化建立索引的重要方法.其實在Lucene中提供了更強大的方法來利用內存建立索引.使用RAMDirectory來替代FSDirectory. 這時所有的索引都將建立在內存當中,這種方法對于數(shù)據(jù)量小的搜索業(yè)務很有幫助,同時可以使用它來進行一些小的測試,避免在測試時頻繁建立刪除索引文件.

在實際應用中RAMDirectory和FSDirectory協(xié)作可以更好的利用內存來優(yōu)化建立索引的時間.

具體方法如下:

1.建立一個使用FSDirectory的IndexWriter

2 .建立一個使用RAMDirectory的IndexWriter

3 把Document添加到RAMDirectory中

4 當達到某種條件將RAMDirectory 中的Document寫入FSDirectory.

5 重復第三步

示意代碼:
???? ?private FSDirectory fsDir = FSDirectory.GetDirectory("index",true);?

? ??? ?private RAMDirectory ramDir = new RAMDirectory();

?????? private IndexWriter fsWriter = IndexWriter(fsDir,new SimpleAnalyzer(), true);
?????? private IndexWriter ramWriter = new IndexWriter(ramDir,new SimpleAnalyzer(), true);
?????? while (there are documents to index)?
????? {
???????? ramWriter.addDocument(doc);
???????? if (condition for flushing memory to disk has been met)?
???????? {
?????????? fsWriter.AddIndexes(Directory[]{ramDir}) ;
?????????? ramWriter.Close();????????? //why not support flush?
?????????? ramWriter =new IndexWriter(ramDir,new SimpleAnalyzer(),true);
???????? }
???? }

這里的條件完全由用戶控制,而不是FSDirectory采用對Document計數(shù)的方式控制何時寫入文件.相比之下有更大的自由性,更能提升性能.

利用RAMDirectory并行建立索引

RAMDirectory還提供了使用多線程來建立索引的可能性.下面這副圖很好的說明了這一點.

?

?

甚至你可以在一個高速的網(wǎng)絡里使用多臺計算機來同時建立索引.就像下面這種圖所示.

?

?

雖然有關并行同步的問題需要你自己進行處理,不過通過這種方式可以大大提高對大量數(shù)據(jù)建立索引的能力.

?

控制索引內容的長度.

在我的一篇速遞介紹過Google Desktop Search只能搜索到文本中第5000個字的.也就是google在建立索引的時候只考慮前5000個字,在Lucene中同樣也有這個配置功能.

Lucene對一份文本建立索引時默認的索引長度是10,000. 你可以通過IndexWriter 的MaxFieldLength屬性對此加以修改.還是用一個例子說明問題.?

???? [Test]
???? public void FieldSize()???????
???? // AddDocuments 和 GetHitCount都是自定義的方法,詳見源代碼
???? {
???????? AddDocuments(dir, 10);???????
???????? //第一個參數(shù)是目錄,第二個配置是索引的長度
???????? Assert.AreEqual(1, GetHitCount("contents", "bridges"))
???????? //原文檔的contents為”Amsterdam has lots of bridges”
???????? //當索引長度為10個字時能找到bridge
???????? AddDocuments(dir, 1);
???????? Assert.AreEqual(0, GetHitCount("contents", "bridges"));
???????? //當索引長度限制為1個字時就無法發(fā)現(xiàn)第5個字bridges
???? }

???對索引內容限長往往是處于效率和空間大小的考慮.能夠對此進行配置是建立索引必備的一個功能.

Optimize?優(yōu)化的是什么?

在以前的例子里,你可能已經(jīng)多次見過writer.Optimize()這段代碼.Optimize到底做了什么?

讓你吃驚的是這里的優(yōu)化對于建立索引不僅沒有起到加速的作用,反而是延長了建立索引的時間.為什么?

因為這里的優(yōu)化不是為建立索引做的,而是為搜索做的.之前我們提到Lucene默認每遇到10個Segment就合并一次,盡管如此在索引完成后仍然會留下幾個segmnets,比如6,7.

而Optimize的過程就是要減少剩下的Segment的數(shù)量,盡量讓它們處于一個文件中.

它的過程很簡單,就是新建一個空的Segmnet,然后把原來的幾個segmnet全合并到這一個segmnet中,在此過程中,你的硬盤空間會變大,因為同時存在兩份一樣大小的索引.不過在優(yōu)化完成后,Lucene會自動將原來的多份Segments刪除,只保留最后生成的一份包含原來所有索引的segment.

盡量減少segments的個數(shù)主要是為了增加查詢的效率.假設你有一個Server,同時有很多的Client建立了各自不同的索引,如果此時搜索,那么必然要同時打開很多的索引文件,這樣顯然會受到很大的限制,對性能產生影響.

當然也不是隨時做Optimize就好,如前所述做優(yōu)化時要花費更多的時間和空間,而且在做優(yōu)化的時候是不能進行查詢的.所以索引建立的后期,并且索引的內容不會再發(fā)生太多的變化的時候做優(yōu)化是一個比較好的時段.

?

Lucene.net?系列四?--- index?本文將介紹有關索引并發(fā)控制的問題,以結束對Lucene.net建立索引問題的討論.

1. 允許任意多的讀操作并發(fā).即可以有任意多的用戶在同一時間對同一份索引做查詢工作.

2.?允許任意多的讀操作在索引被正在被修改的時候進行.即哪怕索引正在被優(yōu)化,添加刪除文檔,這時也是允許用戶對索引進行查詢工作. (it’s so cool.)

3.?同一時間只允許一個對索引修改的操作.即同一時間只允許IndexWriter或IndexReader打開同一份索引.不能允許兩個同時打開一份索引.

Lucene提供了幾種對索引進行讀寫的操作.添加文檔到索引,從索引中刪除文檔,優(yōu)化索引,合并Segments.這些都是對索引進行寫操作的方法. 查詢的時候就會讀取索引的內容.

有關索引并發(fā)的問題是一個比較重要的問題,而且是Lucene的初學者容易忽略的問題,當索引被破壞,或者程序突然出現(xiàn)異常的時候初學者往往不知道是自己的誤操作造成的.

下面讓我們看看Lucene是如何處理索引文件的并發(fā)控制的.

首先記住一下三點準則:

1. 允許任意多的讀操作并發(fā).即可以有任意多的用戶在同一時間對同一份索引做查詢工作.

2.?允許任意多的讀操作在索引被正在被修改的時候進行.即哪怕索引正在被優(yōu)化,添加刪除文檔,這時也是允許用戶對索引進行查詢工作. (it’s so cool.)

3.?同一時間只允許一個對索引修改的操作.即同一時間只允許IndexWriter或IndexReader打開同一份索引.不能允許兩個同時打開一份索引.

第一個準則很容易理解,第二個準則說明Lucene對并發(fā)的操作支持還是不錯的.第三個準則也很正常,不過需要注意的是第三個準則只是表明IndexWriter和IndexReader不能并存,而沒有反對在多線程中利用同一個IndexWriter對索引進行修改.這個功能可是經(jīng)常用到的,所以不要以為它是不允許的.不過這個時候的并發(fā)就需要你自己加以控制,以免出現(xiàn)沖突.

(注: 在前面的系列中已說過IndexReader不是對Index進行讀操作,而是從索引中刪除docuemnt時使用的對象)

有關這三個原則在實際使用Lucene API時候的體現(xiàn),讓我們先看看下面這張表:


表中列出了有關索引的主要讀寫操作.其中空白處表示X軸的操作和Y軸的操作允許并發(fā).

而X處表明X軸的操作和Y軸的操作不允許同時進行.

比如Add document到索引的時候不允許同時從索引中刪除document.

其實以上這張表就是前面三個準則的體現(xiàn).Add Optimize Merge操作都是由IndexWriter來做的.而Delete則是通過IndexReader完成.所以表中空白處正是第一條和第二條準則的體現(xiàn),而X(沖突)處正是第三個原則的具體表現(xiàn).

?

為了在不了解并發(fā)控制的情況下對Lucene API的亂用. Lucene提供了基于文件的鎖機制以確保索引文件不會被破壞.

當你對index 進行修改的時候, 比如添加刪除文檔的時候就會產生 ***write.lock文件,而當你從segment進行讀取信息或者合并segments的時候就會產生***commit.lock文件.在默認情況下,這些文件是放在系統(tǒng)臨時文件夾下的. 簡而言之, write.lock文件存在的時間比較長,也就是對index進行修改的鎖時間比較長,而commit.lock存在的時間往往很短.具體情況見下表.

?

如果索引存在于server, 很多clients想訪問的時候,自然希望能看到其他用戶的鎖文件,這時把鎖文件放到系統(tǒng)臨時文件夾就不好了.此時可以通過配置文件來改變鎖文件存放的位置.

比如在一個asp.net的應用下,你就可以象下面這樣利用web.config文件來實現(xiàn)你的目的.

<configuration>
??? <appSettings>
??????? <add key="Lucene.Net.lockdir" value="c:yourdir" />
??? </appSettings>
</configuration>

不僅如此,在某些情況下比如你的索引文件存放在一個CD-ROM中,這時根本就無法對索引進行修改,也就不存在所謂的并發(fā)沖突,這種情況下你甚至可以講鎖文件的機制取消掉.同樣通過配置文件.

<configuration>
??? <appSettings>
??????? <add key="disableLuceneLocks" value="true" />
??? </appSettings>
</configuration>

不過請注意不要亂用此功能,不然你的索引文件將不再受到安全的保護.

下面用一個例子說明鎖機制的體現(xiàn).

using System;
using System.IO;
using Lucene.Net.Analysis;
using Lucene.Net.Index;
using Lucene.Net.Store;
using NUnit.Framework;
using Directory = Lucene.Net.Store.Directory;


[TestFixture]
public class LockTest
{
?private Directory dir;

?[SetUp]
?public void Init()
?{
? String indexDir = "index";
? dir = FSDirectory.GetDirectory(indexDir, true);
?}

?[Test]
?[ExpectedException(typeof(IOException))]
?public void WriteLock()
?{
? IndexWriter writer1 = null;
? IndexWriter writer2 = null;
? try
? {
?? writer1 = new IndexWriter(dir, new SimpleAnalyzer(), true);
?? writer2 = new IndexWriter(dir, new SimpleAnalyzer(), true);
???
? }
? catch (IOException e)
? {
?? Console.Out.WriteLine(e.StackTrace);
? }
? finally
? {
?? writer1.Close();
?? Assert.IsNull(writer2);
? }
?}

?[Test]
?public void CommitLock()
?{
? IndexReader reader1 = null;
? IndexReader reader2 = null;
? try
? {
?? IndexWriter writer = new IndexWriter(dir, new SimpleAnalyzer(),
??????????????????????????????????????? true);
?? writer.Close();
?? reader1 = IndexReader.Open(dir);
?? reader2 = IndexReader.Open(dir);
? }
? finally
? {
?? reader1.Close();
?? reader2.Close();
? }
?}
}

不過很令人失望的是在Lucene(Java)中應該收到的異常在dotLucene(1.4.3)我卻沒有捕獲到.隨后我在dotLucene的論壇上問了一下,至今尚未有解答.這也是開源項目的無奈了吧.

?

?

Lucene.net?系列五?--- search?在前面的系列我們一直在介紹有關索引建立的問題,現(xiàn)在是該利用這些索引來進行搜索的時候了,Lucene良好的架構使得我們只需要很少的幾行代碼就可以為我們的應用加上搜索的功能,首先讓我們來認識一下搜索時最常用的幾個類.

查詢特定的某個概念

當我們搜索完成的時候會返回一個按Sorce排序的結果集Hits. 這里的Score就是接近度的意思,象Google那樣每個頁面都會有一個分值,搜索結果按分值排列. 如同你使用Google一樣,你不可能查看所有的結果, 你可能只查看第一個結果所以Hits返回的不是所有的匹配文檔本身, 而僅僅是實際文檔的引用. 通過這個引用你可以獲得實際的文檔.原因很好理解, 如果直接返回匹配文檔,數(shù)據(jù)量太大,而很多的結果你甚至不會去看, 想想你會去看Google 搜索結果10頁以后的內容嗎?

下面用一個例子來簡要介紹一下Search

先建立索引

namespace dotLucene.inAction.BasicSearch
{
???? [TestFixture]
???? public class BaseIndexingTestCase
???? {
???????? protected String[] keywords = {"1930110994", "1930110995"};

???????? protected String[] unindexed = {"Java Development with Ant", "JUnit in Action"};

???????? protected String[] unstored = {
????????????? "we have ant and junit",
????????????? "junit use a mock,ant is also",
???????? };

???????? protected String[] text1 = {
???????? ???? "ant junit",
????????????? "junit mock"
???????? };

???????? protected String[] text2 = {
????????????? "200206",
????????????? "200309"
???????? };

???????? protected String[] text3 = {
????????????? "/Computers/Ant", "/Computers/JUnit"
???????? };

???????? protected Directory dir;

???????? [SetUp]
???????? protected void Init()
???????? {
????????????? string indexDir = "index";
????????????? dir = FSDirectory.GetDirectory(indexDir, true);
????????????? AddDocuments(dir);
???????? }

???????? protected void AddDocuments(Directory dir)
???????? {
????????????? IndexWriter writer=new IndexWriter(dir, GetAnalyzer(), true);

????????????? for (int i = 0; i < keywords.Length; i++)
????????????? {

?????????????????? Document doc = new Document();
?????????????????? doc.Add(Field.Keyword("isbn", keywords[i]));
?????????????????? doc.Add(Field.UnIndexed("title", unindexed[i]));
?????????????????? doc.Add(Field.UnStored("contents", unstored[i]));
?????????????????? doc.Add(Field.Text("subject", text1[i]));
?????????????????? doc.Add(Field.Text("pubmonth", text2[i]));
?????????????????? doc.Add(Field.Text("category", text3[i]));
?????????????????? writer.AddDocument(doc);

????????????? }
?????????
????????????? writer.Optimize();
????????????? writer.Close();

???????? }


???????? protected virtual Analyzer GetAnalyzer()
???????? {
????????????? PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(
?????????????????? new SimpleAnalyzer());
????????????? analyzer.AddAnalyzer("pubmonth", new WhitespaceAnalyzer());
????????????? analyzer.AddAnalyzer("category", new WhitespaceAnalyzer());
????????????? return analyzer;
???????? }
???? }
}
這里用到了一些有關Analyzer的知識,將放在以后的系列中介紹.

查詢特定的某個概念

然后利用利用TermQery來搜索一個Term(你可以把它理解為一個Word)

?????????[Test]
???????? public void Term()
???????? {
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Term t = new Term("subject", "ant");
????????????? Query query = new TermQuery(t);
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(1, hits.Length(), "JDwA");

????????????? t = new Term("subject", "junit");
????????????? hits = searcher.Search(new TermQuery(t));
????????????? Assert.AreEqual(2, hits.Length());

????????????? searcher.Close();
???????? }


利用QueryParse簡化查詢語句

顯然對于各種各樣的查詢(與或關系,等等各種復雜的查詢,在下面將介紹),你不希望一一對應的為它們寫出相應的XXXQuery. Lucene已經(jīng)為你考慮到了這點, 通過使用QueryParse這個類, 你只需要寫出我們常見的搜索語句, Lucene會在內部自動做一個轉換.

這個過程有點類似于數(shù)據(jù)庫搜索, 我們已經(jīng)習慣于使用SQL查詢語句,其實在數(shù)據(jù)庫的內部是要做一個轉換的, 因為數(shù)據(jù)庫不認得SQL語句,它只認得查詢語法樹.

讓我們來看一個例子.

???????? [Test]
???????? public void TestQueryParser()
???????? {
????????????? IndexSearcher searcher = new IndexSearcher(directory);

????????????? Query query = QueryParser.Parse("+JUNIT +ANT -MOCK",
????????????? ??????????????????????????????? "contents",
????????????? ??????????????????????????????? new SimpleAnalyzer());
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(1, hits.Length());
????????????? Document d = hits.Doc(0);
????????????? Assert.AreEqual("Java Development with Ant", d.Get("title"));

????????????? query = QueryParser.Parse("mock OR junit",
????????????? ????????????????????????? "contents",
????????????? ????????????????????????? new SimpleAnalyzer());
????????????? hits = searcher.Search(query);
????????????? Assert.AreEqual(2, hits.Length(), "JDwA and JIA");
???????? }

由以上的代碼可以看出我們不需要為每種特定查詢而去設定XXXQuery 通過QueryParse類的靜態(tài)方法Parse就可以很方便的將可讀性好的查詢口語轉換成Lucene內部所使用的各種復雜的查詢語句. 有一點需要注意:在Parse方法中我們使用了SimpleAnalyzer, 這時候會將查詢語句做一些變換,比如這里將JUNIT 等等大寫字母變成了小寫字母,所以才能搜索到(因為我們在建立索引的時候使用的是小寫),如果你將StanderAnalyzer變成WhitespaceAnalyzer就會搜索不到.具體原理以后再說.

+A +B表示A和B要同時存在,-C表示C不存在,A OR B表示A或B二者有一個存在就可以..具體的查詢規(guī)則如下:

?

其中title等等的field表示你在建立索引時所采用的屬性名.

?

?

Lucene.net系列六?-- search?本文主要結合測試案例介紹了Lucene下的各種查詢語句以及它們的簡化方法.

通過本文你將了解Lucene的基本查詢語句,并可以學習所有的測試代碼已加強了解.

源代碼下載

具體的查詢語句

在了解了SQL后, 你是否想了解一下查詢語法樹?在這里簡要介紹一些能被Lucene直接使用的查詢語句.

1.???????? TermQuery
查詢某個特定的詞,在文章開始的例子中已有介紹.常用于查詢關鍵字.

???????????? [Test]
???????? public void Keyword()
???????? {
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Term t = new Term("isbn", "1930110995");
????????????? Query query = new TermQuery(t);
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(1, hits.Length(), "JUnit in Action");
???????? }

注意Lucene中的關鍵字,是需要用戶去保證唯一性的.

?TermQueryQueryParse

只要在QueryParse的Parse方法中只有一個word,就會自動轉換成TermQuery.

2.???????? RangeQuery
用于查詢范圍,通常用于時間,還是來看例子:

namespace dotLucene.inAction.BasicSearch
{
???? public class RangeQueryTest : LiaTestCase
???? {
???????? private Term begin, end;

???????? [SetUp]
???????? protected override void Init()
???????? {
????????????? begin = new Term("pubmonth", "200004");

????????????? end = new Term("pubmonth", "200206");
????????????? base.Init();
???????? }

???????? [Test]
???????? public void Inclusive()
???????? {
????????????? RangeQuery query = new RangeQuery(begin, end, true);
????????????? IndexSearcher searcher = new IndexSearcher(directory);

????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(1, hits.Length());
???????? }

???????? [Test]
???????? public void Exclusive()
???????? {
????????????? RangeQuery query = new RangeQuery(begin, end, false);
????????????? IndexSearcher searcher = new IndexSearcher(directory);

????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(0, hits.Length());
???????? }

???? }
}

RangeQuery的第三個參數(shù)用于表示是否包含該起止日期.

RangeQueryQueryParse

????????????? [Test]
???????? public void TestQueryParser()
???????? {
????????????? Query query = QueryParser.Parse("pubmonth:[200004 TO 200206]", "subject", new SimpleAnalyzer());
????????????? Assert.IsTrue(query is RangeQuery);
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Hits hits = searcher.Search(query);

????????????? query = QueryParser.Parse("{200004 TO 200206}", "pubmonth", new SimpleAnalyzer());
????????????? hits = searcher.Search(query);
????????????? Assert.AreEqual(0, hits.Length(), "JDwA in 200206");
???????? }

Lucene用[] 和{}分別表示包含和不包含.

3.???PrefixQuery

用于搜索是否包含某個特定前綴,常用于Catalog的檢索.

???????????[Test]
???????? public? void? TestPrefixQuery()
???????? {
????????????? PrefixQuery query = new PrefixQuery(new Term("category", "/Computers"));

???????????? ?IndexSearcher searcher = new IndexSearcher(directory);
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(2, hits.Length());
??????????????
????????????? query = new PrefixQuery(new Term("category", "/Computers/JUnit"));
????????????? hits = searcher.Search(query);
????????????? Assert.AreEqual(1, hits.Length(), "JUnit in Action");
???????? }

PrefixQueryQueryParse

??????????? ??[Test]
???????? public void TestQueryParser()
???????? {

????????????? QueryParser qp = new QueryParser("category", new SimpleAnalyzer());
????????????? qp.SetLowercaseWildcardTerms(false);
????????????? Query query =qp.Parse("/Computers*");
????????????? Console.Out.WriteLine("query = {0}", query.ToString());
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(2, hits.Length());
????????????? query =qp.Parse("/Computers/JUnit*");
????????????? hits = searcher.Search(query);
????????????? Assert.AreEqual(1, hits.Length(), "JUnit in Action");
???????? }

這里需要注意的是我們使用了QueryParser對象,而不是QueryParser類. 原因在于使用對象可以對QueryParser的一些默認屬性進行修改.比如在上面的例子中我們的category是大寫的,而QueryParser默認會把所有的含*的查詢字符串變成小寫/computer*. 這樣我們就會查不到原文中的/Computers* ,所以我們需要通過設置QueryParser的默認屬性來改變這一默認選項.即qp.SetLowercaseWildcardTerms(false)所做的工作.

4.????BooleanQuery

用于測試滿足多個條件.

下面兩個例子用于分別測試了滿足與條件和或條件的情況.

???????? [Test]
???????? public void And()
???????? {
????????????? TermQuery searchingBooks =
?????????????????? new TermQuery(new Term("subject", "junit"));

????????????? RangeQuery currentBooks =
?????????????????? new RangeQuery(new Term("pubmonth", "200301"),
?????????????????? ?????????????? new Term("pubmonth", "200312"),
?????????????????? ?????????????? true);
????????????? BooleanQuery currentSearchingBooks = new BooleanQuery();
????????????? currentSearchingBooks.Add(searchingBooks, true, false);
????????????? currentSearchingBooks.Add(currentBooks, true, false);
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Hits hits = searcher.Search(currentSearchingBooks);

????????????? AssertHitsIncludeTitle(hits, "JUnit in Action");
???????? }
???????? [Test]
???????? public void Or()
???????? {
????????????? TermQuery methodologyBooks = new TermQuery(
?????????????????? new Term("category",
?????????????????? ???????? "/Computers/JUnit"));
????????????? TermQuery easternPhilosophyBooks = new TermQuery(
?????????????????? new Term("category",
?????????????????? ???????? "/Computers/Ant"));
????????????? BooleanQuery enlightenmentBooks = new BooleanQuery();
????????????? enlightenmentBooks.Add(methodologyBooks, false, false);
????????????? enlightenmentBooks.Add(easternPhilosophyBooks, false, false);
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Hits hits = searcher.Search(enlightenmentBooks);
????????????? Console.Out.WriteLine("or = " + enlightenmentBooks);
????????????? AssertHitsIncludeTitle(hits, "Java Development with Ant");
????????????? AssertHitsIncludeTitle(hits, "JUnit in Action");

???????? }

什么時候是與什么時候又是或? 關鍵在于BooleanQuery對象的Add方法的參數(shù).

參數(shù)一是待添加的查詢條件.

參數(shù)二Required表示這個條件必須滿足嗎? True表示必須滿足, False表示可以不滿足該條件.

參數(shù)三Prohibited表示這個條件必須拒絕嗎? True表示這么滿足這個條件的結果要排除, False表示可以滿足該條件.

這樣會有三種組合情況,如下表所示:

?

BooleanQueryQueryParse

???????? [Test]
???????? public void TestQueryParser()
???????? {
????????????? Query query = QueryParser.Parse("pubmonth:[200301 TO 200312] AND junit", "subject", new SimpleAnalyzer());
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(1, hits.Length());
????????????? query = QueryParser.Parse("/Computers/JUnit OR /Computers/Ant", "category", new WhitespaceAnalyzer());
????????????? hits = searcher.Search(query);
????????????? Assert.AreEqual(2, hits.Length());
???????? }

注意AND和OR的大小 如果想要A與非B 就用 A AND –B 表示, +A –B也可以.

默認的情況下QueryParser會把空格認為是或關系,就象google一樣.但是你可以通過QueryParser對象修改這一屬性.

[Test]
???????? public void TestQueryParserDefaultAND()
???????? {
????????????? QueryParser qp = new QueryParser("subject", new SimpleAnalyzer());
??????????? ??qp.SetOperator(QueryParser.DEFAULT_OPERATOR_AND );
????????????? Query query = qp.Parse("pubmonth:[200301 TO 200312] junit");
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(1, hits.Length());

???????? }
5.???????? PhraseQuery
查詢短語,這里面主要有一個slop的概念, 也就是各個詞之間的位移偏差, 這個值會影響到結果的評分.如果slop為0,當然最匹配.看看下面的例子就比較容易明白了,有關slop的計算用戶就不需要理解了,不過slop太大的時候對查詢效率是有影響的,所以在實際使用中要把該值設小一點.?PhraseQuery對于短語的順序是不管的,這點在查詢時除了提高命中率外,也會對性能產生很大的影響, 利用SpanNearQuery可以對短語的順序進行控制,提高性能.
????? ??[SetUp]
???? protected void Init()
???? {
???????? // set up sample document
???????? RAMDirectory directory = new RAMDirectory();
???????? IndexWriter writer = new IndexWriter(directory,
???????? ???????????????????????????????????? new WhitespaceAnalyzer(), true);
???????? Document doc = new Document();
???????? doc.Add(Field.Text("field",
???????? ?????????????????? "the quick brown fox jumped over the lazy dog"));
???????? writer.AddDocument(doc);
???????? writer.Close();

???????? searcher = new IndexSearcher(directory);
???? }
??????private bool matched(String[] phrase, int slop)
???? {
???????? PhraseQuery query = new PhraseQuery();
???????? query.SetSlop(slop);

???????? for (int i = 0; i < phrase.Length; i++)
???????? {
????????????? query.Add(new Term("field", phrase[i]));
???????? }

???????? Hits hits = searcher.Search(query);
???????? return hits.Length() > 0;
???? }

???? [Test]
???? public void SlopComparison()
???? {
???????? String[] phrase = new String[]{"quick", "fox"};

???????? Assert.IsFalse(matched(phrase, 0), "exact phrase not found");

???????? Assert.IsTrue(matched(phrase, 1), "close enough");
???? }

?????[Test]
???? public void Reverse()
???? {
???????? String[] phrase = new String[] {"fox", "quick"};

???????? Assert.IsFalse(matched(phrase, 2), "exact phrase not found");

???????? Assert.IsTrue(matched(phrase, 3), "close enough");
???? }

???? [Test]
???? public void Multiple()-
???? {
???????? Assert.IsFalse(matched(new String[] {"quick", "jumped", "lazy"}, 3), "not close enough");
???????? Assert.IsTrue(matched(new String[] {"quick", "jumped", "lazy"}, 4), "just enough");
???????? Assert.IsFalse(matched(new String[] {"lazy", "jumped", "quick"}, 7), "almost but not quite");
???????? Assert.IsTrue(matched(new String[] {"lazy", "jumped", "quick"}, 8), "bingo");
???? }

PhraseQueryQueryParse

利用QueryParse進行短語查詢的時候要先設定slop的值,有兩種方式如下所示

[Test]
???? public void TestQueryParser()
???? {
???????? Query q1 = QueryParser.Parse(""quick fox"",
????????????? "field", new SimpleAnalyzer());
???????? Hits hits1 = searcher.Search(q1);
???????? Assert.AreEqual(hits1.Length(), 0);

???????? Query q2 = QueryParser.Parse(""quick fox"~1",????????? //第一種方式
???????? ??????????????????????????? "field", new SimpleAnalyzer());
???????? Hits hits2 = searcher.Search(q2);
???????? Assert.AreEqual(hits2.Length(), 1);

???????? QueryParser qp = new QueryParser("field", new SimpleAnalyzer());
???????? qp.SetPhraseSlop(1);??????????????????????????????????? //第二種方式
???????? Query q3=qp.Parse(""quick fox"");
???????? Assert.AreEqual(""quick fox"~1", q3.ToString("field"),"sloppy, implicitly");
???????? Hits hits3 = searcher.Search(q2);
???????? Assert.AreEqual(hits3.Length(), 1);
???? }

6.???????? WildcardQuery
通配符搜索,需要注意的是child, mildew的分值是一樣的.?
???????? [Test]
???????? public void Wildcard()
???????? {
????????????? IndexSingleFieldDocs(new Field[]
?????????????????? {
?????????????????????? Field.Text("contents", "wild"),
?????????????????????? Field.Text("contents", "child"),
?????????????????????? Field.Text("contents", "mild"),
?????????????????????? Field.Text("contents", "mildew")
?????????????????? });
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Query query = new WildcardQuery(
?????????????????? new Term("contents", "?ild*"));
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(3, hits.Length(), "child no match");
????????????? Assert.AreEqual(hits.Score(0), hits.Score(1), 0.0, "score the same");
????????????? Assert.AreEqual(hits.Score(1), hits.Score(2), 0.0, "score the same");
???????? }

WildcardQueryQueryParse
需要注意的是出于性能的考慮使用QueryParse的時候,不允許在開頭就使用就使用通配符.
同樣處于性能考慮會將只在末尾含有*的查詢詞轉換為PrefixQuery.
???????? [Test, ExpectedException(typeof (ParseException))]
???????? public void TestQueryParserException()
???????? {
????????????? Query query = QueryParser.Parse("?ild*", "contents", new WhitespaceAnalyzer());
???????? }

???????? [Test]
???????? public void TestQueryParserTailAsterrisk()
???????? {
????????????? Query query = QueryParser.Parse("mild*", "contents", new WhitespaceAnalyzer());
????????????? Assert.IsTrue(query is PrefixQuery);
????????????? Assert.IsFalse(query is WildcardQuery);

???????? }

???????? [Test]
???????? public void TestQueryParser()
???????? {
????????????? Query query = QueryParser.Parse("mi?d*", "contents", new WhitespaceAnalyzer());
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(2, hits.Length());
???????? }
7.???????? FuzzyQuery
模糊查詢, 需要注意的是兩個匹配項的分值是不同的,這點和WildcardQuery是不同的

???????? [Test]
???????? public void Fuzzy()
???????? {
????????????? Query query = new FuzzyQuery(new Term("contents", "wuzza"));
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual( 2, hits.Length(),"both close enough");
????????????? Assert.IsTrue(hits.Score(0) != hits.Score(1),"wuzzy closer than fuzzy");
????????????? Assert.AreEqual("wuzzy", hits.Doc(0).Get("contents"),"wuzza bear");
???????? }


FuzzyQuery
QueryParse

注意和PhraseQuery中表示slop的區(qū)別,前者~后要跟數(shù)字.

???????? [Test]
???????? public void TestQueryParser()
???????? {
????????????? Query query =QueryParser.Parse("wuzza~","contents",new SimpleAnalyzer());
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual( 2, hits.Length(),"both close enough");
???????? }

轉載于:https://www.cnblogs.com/huideng/p/3979307.html

總結

以上是生活随笔為你收集整理的(转)Lucene的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。

99精品在线视频播放 | 91黄视频在线 | 久久精品2 | 日韩91在线 | 国产精品久久久久久久久久免费看 | 国产97在线观看 | 五月婷婷导航 | 国产99精品在线观看 | 亚洲高清在线观看视频 | 免费看的av片 | 国产专区视频在线 | 91亚洲在线观看 | 午夜精品一区二区三区可下载 | 国产中文字幕精品 | 欧美va天堂va视频va在线 | 欧美性生活免费 | 中文字幕亚洲国产 | 久久精品中文字幕免费mv | 亚洲精品国产精品乱码不99热 | 亚洲a网 | www.黄色小说.com | 日韩精品中文字幕在线 | 丁香六月久久综合狠狠色 | 亚洲成a人片77777kkkk1在线观看 | 99精品欧美一区二区三区 | 国产精久久 | 9在线观看免费高清完整版在线观看明 | 日韩一区二区三区观看 | 久久国产网 | 欧美小视频在线观看 | 欧美大片大全 | 日韩av影视在线 | 欧美日韩国产精品久久 | 深爱激情五月婷婷 | 国产精品露脸在线 | 国产在线播放一区 | 久久久久久久久久久久久久电影 | 日本视频不卡 | 亚洲精品国产精品99久久 | 日本中文字幕观看 | 久久久精品欧美一区二区免费 | 成年人av在线播放 | 国产色妞影院wwwxxx | 国产午夜免费视频 | 成人在线观看你懂的 | 欧美日韩国产伦理 | www免费黄色 | 精品在线看 | 97天天干| 91日韩国产| 在线免费中文字幕 | 9免费视频| 日日夜夜免费精品视频 | 婷婷综合伊人 | 最新国产在线视频 | 一区二区精品在线 | 久久社区视频 | 91成人免费看 | 免费三级网| 在线免费看黄色 | 国产在线视频在线观看 | 探花视频在线观看+在线播放 | 成人免费在线视频 | a级国产乱理论片在线观看 伊人宗合网 | 91免费高清视频 | 欧美亚洲一级片 | 啪嗒啪嗒免费观看完整版 | 国产专区精品视频 | 成人久久18免费网站 | 97在线观看免费观看 | 国产精品一区二区免费看 | 2023亚洲精品国偷拍自产在线 | 成人在线视 | 国产精品欧美激情在线观看 | 日韩中文字幕91 | 美女性爽视频国产免费app | 国产+日韩欧美 | 成人影视免费 | 天天综合视频在线观看 | 国产日韩在线播放 | 国产xxxx性hd极品 | 国产精品欧美久久久久三级 | 久久影院精品 | 天天艹天天 | 婷婷丁香七月 | 9999在线 | 亚洲成a人片在线观看中文 中文字幕在线视频第一页 狠狠色丁香婷婷综合 | 成人一级电影在线观看 | japanesexxxxfreehd乱熟| 在线免费性生活片 | 麻豆视频大全 | 91av大全 | 99人成在线观看视频 | 91香蕉视频 | 超碰人人在线 | 成人在线中文字幕 | 麻豆精品传媒视频 | 亚洲第一av在线 | 免费观看午夜视频 | 久久国产一二区 | 精品国产大片 | 久久精品电影院 | 爱爱av网站| 91九色免费视频 | 国产成人av在线 | 中文字幕日韩高清 | 日日躁夜夜躁xxxxaaaa | 一区二区三区免费在线观看 | 日韩高清成人在线 | 国产vs久久 | 亚洲免费视频观看 | www.午夜 | 日韩午夜小视频 | 欧美激情视频在线免费观看 | 中文字幕日本特黄aa毛片 | 激情欧美在线观看 | av大全在线看 | 日韩a在线 | 免费日韩 精品中文字幕视频在线 | 在线视频一区二区 | 操操爽| 午夜影院在线观看18 | 日韩精品在线免费观看 | 国产精品mv | 成人一区二区三区中文字幕 | 99久久精品一区二区成人 | 久久精品99国产国产 | 五月婷婷激情网 | 色视频网站免费观看 | 99精品系列| 天天射天天干天天插 | 亚洲视频,欧洲视频 | 久草综合视频 | 嫩草伊人久久精品少妇av | 亚洲精品福利在线 | 亚洲国产电影在线观看 | 在线精品亚洲一区二区 | 国内精品久久久久影院一蜜桃 | 国产超碰在线观看 | 在线观看色视频 | 免费视频黄色 | 九九久久久久久久久激情 | 国产日韩中文字幕 | 人人舔人人射 | 国产精品初高中精品久久 | 欧美日韩精品在线一区二区 | 99精品久久久久 | 国产无遮挡又黄又爽在线观看 | 4p变态网欧美系列 | 久久人人爽人人人人片 | 成人黄色大片在线免费观看 | 亚洲精品免费播放 | 欧美成人精品在线 | 91chinese在线 | 日韩美一区二区三区 | 亚洲人久久久 | 日韩精品第一区 | 久草在线视频中文 | 九草视频在线观看 | 欧美日韩中文在线视频 | www.夜夜操.com | 狠狠撸电影 | 96亚洲精品久久 | 国产精品原创av片国产免费 | 97电影手机版 | 欧美久草网 | 国产日韩精品一区二区 | 久久国产三级 | 69av视频在线 | 日韩中文在线观看 | 国产精品麻豆果冻传媒在线播放 | 最新中文字幕 | 午夜精品一区二区三区在线 | 香蕉视频在线播放 | 狠狠狠色丁香婷婷综合久久五月 | av不卡中文字幕 | 伊人中文字幕在线 | 免费黄a | 6080yy精品一区二区三区 | 亚洲aⅴ免费在线观看 | 伊人伊成久久人综合网小说 | www.天天色.com | 中文字幕国语官网在线视频 | 激情影音先锋 | 亚洲 中文 欧美 日韩vr 在线 | 亚洲精品中文字幕视频 | 亚洲精品乱码久久久久久蜜桃不爽 | 亚洲黄色免费网站 | 色综合久久综合网 | 欧亚日韩精品一区二区在线 | 在线观看一区视频 | 国产高清中文字幕 | 日韩视频欧美视频 | 日日干美女| 午夜影院先 | 亚洲三级国产 | 99产精品成人啪免费网站 | 国产一级电影免费观看 | 中文字幕综合在线 | 日本丶国产丶欧美色综合 | 在线免费观看黄色小说 | 久久艹在线 | 久久精品99久久 | 久久久久久久av | 国产精品一区二区久久 | 欧美日韩视频一区二区三区 | 黄色a级片在线观看 | 国产一区二区三区网站 | 国产原创中文在线 | av久久在线| 92精品国产成人观看免费 | a级一a一级在线观看 | 人人爽人人做 | 91黄色视屏 | 国产精品久久久久久久久久久久冷 | 精品一区二区免费视频 | 久久免费看a级毛毛片 | 国偷自产中文字幕亚洲手机在线 | 午夜电影久久久 | 日韩精品中文字幕有码 | 久久视频在线观看免费 | 伊人中文字幕在线 | 香蕉免费 | 涩五月婷婷 | 九九热国产视频 | 免费日韩一区二区三区 | 天天操天天干天天干 | 国产h在线播放 | 欧美尹人 | 激情av资源 | 日韩视频一区二区三区在线播放免费观看 | 91久久奴性调教 | 日b视频在线观看网址 | 成人免费网站视频 | 在线精品观看 | 久久老司机精品视频 | 久久久久免费精品国产小说色大师 | 在线观看日韩国产 | 免费在线观看视频一区 | 久久免视频 | 日韩欧美高清视频在线观看 | 国产精品不卡在线 | 成人在线播放av | 久草在线欧美 | 天天操天天操天天操天天操 | 久草在线视频在线 | 超碰国产97 | 999成人 | 天天插天天射 | 免费三级黄色 | 国产精品视频 | 国产中文字幕在线看 | 久久线视频 | 4hu视频 | 日韩在线 一区二区 | 波多野结衣一区二区三区中文字幕 | 国产亚洲精品成人 | av网站在线观看免费 | 欧美一级视频在线观看 | 久久久久久久久毛片精品 | 在线亚洲免费视频 | 中文字幕免费高清在线观看 | 中文字幕色婷婷在线视频 | av在线电影免费观看 | 日本亚洲国产 | 91在线视频 | 91色在线观看视频 | 91成人天堂久久成人 | 97在线看片 | 黄色a视频| 国产视频高清 | 月丁香婷婷 | 夜夜夜影院 | 日韩v在线91成人自拍 | 91视频在线观看免费 | 日韩在线视频一区二区三区 | 国产中文字幕91 | 一区二区三区四区在线 | 国产91在线看 | 免费在线观看a v | 日韩午夜电影网 | 又黄又刺激 | 久久久污| 婷婷成人综合 | av电影久久 | 草 免费视频 | 日韩网站免费观看 | 国产一级黄色免费看 | 亚洲伦理一区二区 | 超碰在线97免费 | av三级av| 欧美91成人网 | 伊人亚洲精品 | 91一区一区三区 | 久久电影网站中文字幕 | 96精品视频 | 国产精品99久久久久久人免费 | 国产免费二区 | 一区二区中文字幕在线观看 | 精品女同一区二区三区在线观看 | 亚洲精品国产区 | av成人免费网站 | 久久草网 | 精品久久久久久久久亚洲 | 精品美女久久久久久免费 | 在线黄色观看 | 久草av在线播放 | 午夜精品久久久久久久99 | 美女露久久 | 人人要人人澡人人爽人人dvd | 国产91精品一区二区麻豆网站 | 午夜狠狠干 | 91精品爽啪蜜夜国产在线播放 | 美女黄色网在线播放 | 国产精品成人一区二区三区吃奶 | av蜜桃在线| 黄色国产精品 | 国产视频二区三区 | 毛片美女网站 | 久草视频在线资源 | 麻豆传媒在线免费看 | 欧美另类交人妖 | 国产福利电影网址 | 91精品视频播放 | 久久午夜国产精品 | 精品视频不卡 | 国产正在播放 | 婷婷av资源| 国产在线高清视频 | 中文一区在线 | 欧美日韩国产一二 | 久久一本综合 | 精品久久久久久电影 | 福利一区在线视频 | 久久久免费毛片 | 天堂av一区二区 | 高清精品在线 | 激情欧美一区二区三区免费看 | 日韩视频在线一区 | 日韩中文字幕免费 | 91av免费观看 | 日本激情视频中文字幕 | 国产免费嫩草影院 | 日韩欧美有码在线 | 中文字幕成人在线 | 少妇搡bbbb搡bbb搡忠贞 | 天堂av在线网站 | 国产精品夜夜夜一区二区三区尤 | 欧美日韩一区二区三区在线观看视频 | 99热超碰在线 | 特级西西444www高清大视频 | 精品亚洲va在线va天堂资源站 | 探花视频免费观看 | 久久精品视频99 | 午夜精品久久久99热福利 | 尤物97国产精品久久精品国产 | 国产精品亚洲精品 | 亚洲精品美女久久久久网站 | 黄色的视频网站 | 国产黄色在线看 | 国产黄色片免费 | 安徽妇搡bbbb搡bbbb | 日韩欧美综合在线视频 | 婷婷av综合 | 在线观看视频你懂的 | 亚洲免费在线视频 | 日韩电影精品 | 亚洲综合五月天 | 在线亚洲天堂网 | 免费国产ww| 久久免费电影 | 91大神视频网站 | 蜜臀一区二区三区精品免费视频 | www.啪啪.com| 久草五月 | 久久夜视频 | 永久免费的av电影 | 久久涩涩网站 | 九九视频免费 | av亚洲产国偷v产偷v自拍小说 | 国产精品久久久区三区天天噜 | a级一a一级在线观看 | 热re99久久精品国产66热 | 99热最新地址| 最近免费中文字幕大全高清10 | 色婷婷成人网 | 在线激情av电影 | 在线观看免费91 | 婷婷中文在线 | 国产视频在线观看免费 | 在线观看电影av | 草樱av | 欧美日韩国产综合一区二区 | 久久久久久久久久久网 | 在线观看av的网站 | 97人人模人人爽人人喊网 | 右手影院亚洲欧美 | 久免费视频 | 99自拍视频在线观看 | 亚洲视频六区 | 色婷婷骚婷婷 | 天天操天天插 | 蜜桃传媒一区二区 | 中文亚洲欧美日韩 | 久久av伊人 | 91色国产在线 | 国产精品字幕 | 在线观看视频一区二区 | 美女视频黄免费网站 | 天天色草 | 成人午夜在线电影 | 久久久激情视频 | 99久久日韩精品视频免费在线观看 | 狠狠狠狠狠色综合 | 色免费在线| 国产高清一区二区 | 西西444www| 久久久久久片 | 国产一级在线观看 | 久久视频在线免费观看 | 特级黄色视频毛片 | 亚洲精品456在线播放乱码 | 在线免费色视频 | 中文字幕中文字幕在线一区 | 成年人精品 | 国产黄色美女 | 免费看毛片在线 | 久久久久一区二区三区四区 | 日本高清dvd| 一级电影免费在线观看 | 色综合天天 | 一 级 黄 色 片免费看的 | 91麻豆精品国产91久久久无限制版 | 久久精品国产成人精品 | 久久亚洲人 | 久久高清精品 | 久久免费电影网 | 国产精品第一视频 | 亚洲欧洲成人 | 香蕉免费 | 天堂av在线网 | 久久国色夜色精品国产 | 免费在线观看亚洲视频 | 中文字幕日本电影 | 亚洲成人频道 | 久久呀 | 波多野结衣在线播放视频 | 狠狠干网站 | 丁香激情视频 | 超碰在线人人艹 | 免费特级黄色片 | 91精品久久久久 | 九九精品视频在线看 | 怡红院久久 | 国产精品第十页 | 国产又粗又猛又色又黄视频 | 亚洲精品国产高清 | 狠狠ri| 在线观看免费中文字幕 | 亚洲精品黄网站 | 在线黄色免费av | 91黄色在线视频 | 国产视频在线观看一区 | 国产精品 9999 | 国产精品乱码久久久久久1区2区 | 一级黄色av | 久久综合九色综合97婷婷女人 | 欧洲精品久久久久毛片完整版 | 激情偷乱人伦小说视频在线观看 | 黄色av电影在线观看 | 黄色片免费电影 | 激情中文字幕 | 亚洲h色精品 | 久久久久一区 | 五月婷婷六月丁香激情 | 超碰个人在线 | 在线观看成人网 | 日日干天天爽 | 亚洲做受高潮欧美裸体 | 亚洲 中文 欧美 日韩vr 在线 | 日韩中文字幕a | 在线观看亚洲精品视频 | 天天干 天天摸 天天操 | 欧美 日韩 成人 | 亚洲欧美国产精品久久久久 | 成人午夜剧场在线观看 | 久久超 | 亚洲黄污| 97免费在线观看视频 | 国产aaa毛片| 99视频| 深夜激情影院 | 97视频在线播放 | 波多野结衣亚洲一区二区 | 日韩电影中文,亚洲精品乱码 | 亚洲狠狠操 | 999久久a精品合区久久久 | 国产高清av免费在线观看 | 欧美高清视频不卡网 | 国产亚洲精品美女久久 | 99免费在线观看视频 | 日韩超碰在线 | www成人av| 天天激情综合 | 亚洲午夜小视频 | 欧美一级欧美一级 | 波多野结衣视频一区二区 | www狠狠| 国产日韩欧美视频 | 久久超碰网 | 五月婷婷婷婷婷 | 日韩中文字幕亚洲一区二区va在线 | 五月网婷婷 | 激情视频一区二区三区 | 精品中文字幕在线播放 | 蜜臀av网址| sm免费xx网站 | 波多野结衣日韩 | 九九色在线 | 怡红院av久久久久久久 | 欧美污网站 | 人人插超碰 | 九九九九热精品免费视频点播观看 | 日韩av中文在线观看 | 亚洲日本色 | jizz欧美性9| 国产成人精品av久久 | 免费精品在线观看 | 欧美黑人巨大xxxxx | 国产视频观看 | 国产成人久久精品77777 | 青青草在久久免费久久免费 | 在线观看视频一区二区 | 国产精品欧美一区二区 | 夜夜操网站 | 人人舔人人干 | 亚洲精品mv在线观看 | www.午夜| av在线电影免费观看 | 91视频三区 | 国产成人在线免费观看 | 中文在线字幕免费观 | 久久国际影院 | 久草综合在线 | 国产成人综合在线观看 | 久久短视频 | 96av视频 | 久热免费在线观看 | 中文字幕视频在线播放 | 亚洲视屏一区 | 欧美三级在线播放 | 91精品免费在线观看 | 在线三级av | 久久老司机精品视频 | 欧美一二三区在线播放 | 最新中文字幕在线观看视频 | 六月丁香激情综合色啪小说 | 国产成人三级一区二区在线观看一 | 国产精品1区2区 | 91av在线免费观看 | 99热在线观看免费 | 日韩中文字幕免费视频 | 一区视频在线 | av日韩中文 | 13日本xxxxxⅹxxx20 | 久久久免费观看完整版 | 欧美激情视频在线免费观看 | 91久久人澡人人添人人爽欧美 | 国产精品久久久久免费观看 | 韩日电影在线观看 | 永久中文字幕 | 日批网站免费观看 | 色婷婷一 | 久久人人做 | 欧美日高清视频 | 98超碰人人| 亚洲国产人午在线一二区 | 91免费在线看片 | 亚洲精品天天 | 亚洲一区免费在线 | 日本黄色免费大片 | 婷婷丁香花五月天 | 97激情影院 | av黄色av| 高清有码中文字幕 | 精品久久久久免费极品大片 | 国产精品久久久久久久久久久久久久 | 欧美午夜理伦三级在线观看 | 麻豆视频免费版 | 免费黄色看片 | 亚洲最新av在线网站 | 亚洲一区二区精品视频 | 午夜三级在线 | 中文字幕在线观看网址 | 久久色视频 | 亚洲一二视频 | 涩涩网站在线观看 | 精品国产一区二区三区噜噜噜 | 久久免费大片 | 成人国产一区 | 中文字幕有码在线观看 | 在线观看免费av网站 | 亚洲天堂网站视频 | 91你懂的 | 福利视频区 | 日韩av黄 | 天天射综合网视频 | 在线视频观看成人 | 欧美精品被 | 精品在线观看免费 | 亚洲精品国产精品国自 | 精品国产成人av | 国产精品久久久区三区天天噜 | 久久九九免费视频 | 在线观看黄色小视频 | 久久久黄色免费网站 | 国产一区二区精 | 成年人在线免费看视频 | 成人蜜桃网 | 婷婷综合在线 | 欧美 日韩 性 | 一区二区精品在线视频 | 521色香蕉网站在线观看 | 国产1区2 | 久久视频这里有久久精品视频11 | 久久久国产精品免费 | 色姑娘综合 | 精品久久网 | 久草影视在线观看 | 久福利 | 在线观看中文字幕一区 | 国产一区二区播放 | 欧美精品一区在线 | 国产不卡精品视频 | 国产黄色资源 | 国产成人av电影在线观看 | 亚洲欧美成人在线 | 久久久久国产一区二区 | 亚洲在线视频免费 | 一本到视频在线观看 | 欧美精品免费在线观看 | 在线黄色国产电影 | 久久精品国产一区二区三 | 91av在线不卡 | 亚洲乱码精品久久久 | 久草视频中文 | 日日操天天操狠狠操 | 国产一级一级国产 | 97精品国产 | 国产一区二区中文字幕 | 日韩在线在线 | 性日韩欧美在线视频 | 国产久草在线 | 99精品视频免费观看 | 99热这里是精品 | 在线视频 影院 | 欧美日韩国产高清视频 | 91激情视频在线播放 | 在线看黄色的网站 | 激情视频免费观看 | 亚洲小视频在线 | 天天艹天天干天天 | 久久公开视频 | 久久国产亚洲视频 | 中文字幕在线视频精品 | 成年人黄色av | 中日韩三级视频 | 日日操日日插 | 欧美美女视频在线观看 | 激情欧美日韩一区二区 | 日韩在线免费不卡 | 91在线视频免费 | 奇米影视四色8888 | 中文字幕免费观看全部电影 | 日本黄色免费在线 | 中文字幕在线成人 | 国产成人专区 | 中文字幕免费观看全部电影 | 伊人久久av | 久久公开视频 | 韩日电影在线 | 美女免费网视频 | 国产在线一线 | 81精品国产乱码久久久久久 | 亚洲专区中文字幕 | 国产亚洲精品久久久久久 | 91丨九色丨91啦蝌蚪老版 | 久久久精品视频网站 | 久久免费看av | 国产精品久久久久久久久久免费看 | 久要激情网 | 中文字幕日本电影 | 美女露久久 | 五月色婷 | 九九久久免费视频 | 激情五月播播久久久精品 | 青青网视频| 成人黄在线 | 奇米影视777四色米奇影院 | 欧美人体xx | 黄色片毛片 | 欧美日韩在线观看视频 | 伊人网综合在线观看 | 久久ww| 三级黄色片在线观看 | 亚洲国产激情 | 一区二区三区日韩精品 | 天堂网一区二区 | av免费电影在线 | 国产精品免费在线观看视频 | 91在线资源 | 久久成人欧美 | 狠狠操狠狠干天天操 | 国产视频综合在线 | 97在线观看免费高清 | 天天射天| 麻豆视频一区二区 | 国产黄色片一级 | 国产精品国产三级在线专区 | 亚洲日本黄色 | 麻豆手机在线 | 国产群p | 天天插天天干天天操 | 亚洲成aⅴ人片久久青草影院 | 九九精品视频在线看 | 超碰97免费 | 一区二区三区免费在线观看 | 精品久久福利 | 黄色在线观看污 | 天天爽天天碰狠狠添 | 亚洲欧美成aⅴ人在线观看 四虎在线观看 | 2018好看的中文在线观看 | 精品国产一区二区三区久久久 | 婷婷色六月天 | 国产成人av | 久久久久久免费毛片精品 | 亚洲精品日韩在线观看 | 五月婷婷亚洲 | 中文字幕91 | 成人午夜影院在线观看 | 高清中文字幕 | 日韩字幕 | 婷婷六月天在线 | 看毛片网站 | 久久国产精品偷 | 欧美一区二区三区四区夜夜大片 | 婷婷色网视频在线播放 | 欧美日韩久久不卡 | 五月亚洲综合 | 国产不卡毛片 | 久久九九影视 | 在线观看黄色大片 | 亚洲综合日韩在线 | 日韩乱理 | 一级片免费观看视频 | www.天天色.com | 中文在线资源 | 91成人免费看 | 久久99热这里只有精品国产 | 成人av片在线观看 | 2023年中文无字幕文字 | 日韩在线观看三区 | 国产视频一区二区在线播放 | 亚洲区视频在线 | 国模吧一区 | 在线a人片免费观看视频 | 一级黄色片在线免费看 | 国内久久久久久 | 日韩免费电影在线观看 | 美女视频黄是免费的 | 99精品国自产在线 | 日本性生活免费看 | 午夜精品久久久久久 | 国产亚洲资源 | 国产亚洲精品免费 | 国产精品高潮呻吟久久av无 | 欧美精品一区二区免费 | 激情综合站 | 天天干国产| 日韩一级黄色大片 | 天天摸日日操 | 色婷婷狠狠五月综合天色拍 | 在线日韩精品视频 | 国产乱对白刺激视频不卡 | 日日操日日 | 五月天综合色激情 | 久久99国产一区二区三区 | 国产高清在线不卡 | 国产无遮挡又黄又爽馒头漫画 | www日韩精品 | 免费观看av网站 | www.久热 | 久草在线看片 | 日韩二区三区 | 国产拍在线 | 免费在线成人av | 免费黄av| 日韩a级黄色片 | 综合精品久久久 | 日韩久久一区二区 | 99视频精品在线 | 欧美在线视频免费 | 色综合久久88色综合天天人守婷 | 亚州人成在线播放 | 国产传媒一区在线 | 国产精品综合久久久久 | 91视频88av | 97国产大学生情侣白嫩酒店 | 91高清免费看 | 成人18视频| 91少妇精拍在线播放 | 免费中文字幕视频 | 亚洲一级国产 | 在线观看一区 | 中文免费在线观看 | 久草精品电影 | 国产一区二区在线视频观看 | 亚洲成a人片综合在线 | 在线视频 区 | 91成熟丰满女人少妇 | 午夜精品视频一区二区三区在线看 | 黄色网址中文字幕 | 亚洲精品在线视频 | 久久成人亚洲欧美电影 | 亚在线播放中文视频 | 在线看片中文字幕 | 最新国产精品拍自在线播放 | 黄色小说视频网站 | 808电影 | 久久成人国产精品一区二区 | 色综合天天色综合 | 91丨九色丨蝌蚪丨对白 | 日韩av黄| 免费在线观看中文字幕 | 欧美精品午夜 | 中文字幕刺激在线 | 国产视频在线免费观看 | 亚洲精选国产 | 日韩电影一区二区三区在线观看 | 国产成人精品电影久久久 | 欧美国产日韩一区二区 | 天天综合久久 | 亚洲年轻女教师毛茸茸 | 亚洲欧美色婷婷 | 亚洲精品免费在线观看视频 | 91久草视频| 久草在线一免费新视频 | 国产成人精品一区二区三区在线观看 | 2021av在线| 有码中文字幕在线观看 | 国产成人精品久久久久 | 日韩专区 在线 | 日日草视频 | 国产一级黄色片免费看 | 精品91视频| 黄av在线 | 九九九热 | 精品久久美女 | 婷婷色在线 | 国产精品一区二区免费 | 91av视频在线免费观看 | 亚洲精品视频免费观看 | 国产精品免费在线播放 | 久久精品官网 | 婷婷成人亚洲综合国产xv88 | 国产精品嫩草影院99网站 | 久久精品亚洲一区二区三区观看模式 | 久久人人爽人人片 | 国产成人精品一区二区三区免费 | 国产精品1000 | 欧美三级免费 | 久草在线资源观看 | 国产九九精品 | 久久美女免费视频 | 国产精品婷婷 | 一区二区日韩av | 亚洲成 人精品 | 成年人黄色在线观看 | 丁香六月中文字幕 | 五月婷婷丁香在线观看 | 亚洲欧洲精品一区二区 | 国产视频二区三区 | 在线草 | 色婷婷在线播放 | 日日摸日日爽 | 亚洲日本色 | 久久成人亚洲欧美电影 | 国产精品一区在线播放 | 天天操天天射天天爽 | 亚洲精品9 | 精品在线播放视频 | 亚洲午夜电影网 | 久久精品电影院 | 亚洲精品乱码久久久久久蜜桃不爽 | 久久精品99国产精品酒店日本 | 黄色片亚洲 | 亚洲一级黄色大片 | 欧美日韩在线免费观看视频 | 国产精品久久久久久久久久免费看 | 在线小视频 | 51精品国自产在线 | 狠狠干美女 | 免费网站黄色 | 精品在线视频观看 | 麻豆国产在线视频 | 一二三区av | 免费视频一级片 | 久久综合福利 | 在线免费亚洲 | 久草视频播放 | 在线观看中文字幕一区 | 欧美一级在线 | 一区二区三区四区在线免费观看 | 久草在在线 | 五月婷婷黄色网 | 97超碰超碰 | 91麻豆国产| 国产精品正在播放 | 欧美美女视频在线观看 | 久久综合九色综合97_ 久久久 | 在线韩国电影免费观影完整版 | 91福利区一区二区三区 | 麻豆国产电影 | 500部大龄熟乱视频使用方法 | 国产精品99久久免费黑人 | 精品视频在线免费观看 | 国产一区成人 | 在线国产专区 | 免费a网址 | 久久视频一区 | 免费日韩一区二区三区 | 天天操天天操天天操 | avhd高清在线谜片 | 99久高清在线观看视频99精品热在线观看视频 | 中文字幕在线观 | 韩国av永久免费 | 亚洲 欧美 变态 国产 另类 | 欧美成天堂网地址 | 国产精品自在线拍国产 | 国产在线观看污片 | 国产成人精品aaa | 亚洲欧美综合精品久久成人 | 日韩av电影免费在线观看 | 午夜久久影视 | 久99久中文字幕在线 | 99精品欧美一区二区蜜桃免费 | 色婷婷99 | 欧美日韩免费在线观看视频 | 日本中文字幕在线一区 | 久草久热 | 天天干天天插伊人网 | 视频成人永久免费视频 | 一区二区视频电影在线观看 | 国产99久久久久久免费看 | 免费中文字幕在线观看 | 欧美日韩午夜爽爽 | av 一区 二区 久久 | 97精品久久 | 久久综合九色综合久久久精品综合 | 狠狠色噜噜狠狠狠狠2022 | 蜜臀aⅴ精品一区二区三区 久久视屏网 | 日日插日日干 | 欧美日韩精品在线免费观看 | 国产精品欧美久久久久无广告 | 国产午夜三级一区二区三 | 国内精品久久久久久久 | 久久精品一二三区白丝高潮 | av性网站 | 中文区中文字幕免费看 | 久久九九久久 | 91精品国产欧美一区二区成人 | 欧美日韩中文字幕在线视频 | 欧美日韩精品二区第二页 | 欧美在线观看小视频 | 久久久久亚洲天堂 | 亚洲一区在线看 | 夜夜爱av | 国产精品一区二区久久国产 | 五月天婷婷在线视频 | 亚洲精品伦理在线 | 国产精品久久久久久久久久久久午夜片 | 国产91在线观看 | 精品视频免费久久久看 | 美国人与动物xxxx | 国产亚洲人成网站在线观看 | 国产在线精品国自产拍影院 | 波多野结衣小视频 | 97电院网手机版 | 一区二区精品 | 亚洲精品国产精品国产 | 91av电影网 | 人人搞人人爽 | 久久成人人人人精品欧 | 中文字幕乱偷在线 | 亚洲天堂网在线视频观看 | 久久ww| 在线观看免费 | www.91成人|