Lucene 3.5.0学习笔记

http://blog.csdn.net/huanhuan_tiantian/article/details/7403481

Lucene 3.5.0学习笔记

1. 整个全文搜索引擎就是建立索引,查找索引的一个过程。

2. 几个重要的概念:

Directory:目录(分为RAMdirectory 和 FSDdirectory ,用于存放文件和索引的目录)

Analyzer:分词器,一般用标准分词器(standardAnalyzer就可以啦)

IndexWriter:写索引

IndexReader:读索引

IndexSearcher:搜索索引

Document:相当于数据库里的一条记录

Field:相当于数据库里的一条记录的字段

3. 建立索引:

public void createIndex(){

IndexWriter writer = null;

try {

//1.创建一个索引目录

//Directory dir = new RAMDirectory();   //建立在内存中

Directory dir = FSDirectory.open(new File("D:\\Documents\\Desktop\\新建文件夹\\myIndex"));//建立在硬盘上

//2.创建一个indexWriter

IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_35new StandardAnalyzer(Version.LUCENE_35));

writer = new IndexWriter(dir, iwc);

//3.创建一个document

Document document ;

for(File file : new File("D:\\Documents\\Desktop\\新建文件夹\\myFile").listFiles()){

document  = new Document();

document.add(new Field("content",new FileReader(file)));

document.add(new Field("path",file.getAbsolutePath(),Field.Store.YES,Field.Index.NOT_ANALYZED));

document.add(new Field("name",file.getName(),Field.Store.YES,Field.Index.NOT_ANALYZED));

//4.利用indexWriter将索引写入到目录中

writer.addDocument(document);

}

catch (Exception e) {

e.printStackTrace();

finally{

if(writer != null){

try {

writer.close();

catch (CorruptIndexException e) {

e.printStackTrace();

catch (IOException e) {

e.printStackTrace();

}

}

}

}

4. 查找索引:

public void searchIndex(){

Directory dir;

try {

//1.创建一个目录

dir = FSDirectory.open(new File("D:\\Documents\\Desktop\\新建文件夹\\myIndex"));//建立在硬盘上

//2.创建一个indexReader

IndexReader reader = IndexReader.open(dir);

//3.创建一个indexSearcher

IndexSearcher searcher = new IndexSearcher(reader);

//4.创建一个queryParser(第二个参数为要搜索的域,即条件)

QueryParser parser = new QueryParser(Version.LUCENE_35"content"new StandardAnalyzer(Version.LUCENE_35));

//5.创建一个query(参数为要搜索的指,即content=“中华”)

Query query = parser.parse("JAVA");  

//6.根据searcher搜索并返回一个TopDocs

TopDocs docs = searcher.search(query, 10); //搜索前10条记录

//7.根据TopDocs获取一个ScoreDoc数组对象

ScoreDoc[] sds = docs.scoreDocs;

Document document ;

for(ScoreDoc sd : sds){

//8.创建一个document

document = searcher.doc(sd.doc);  //根据编号为sd.doc查找

System.out.println("path:"+document.get("path")+"--------   name:"+document.get("name"));

}

//9.关闭流

reader.close();

catch (Exception e) {

e.printStackTrace();

}

}

5. 关于域Field的几个选项说明:

Field.Store.YES或者NO(存储域选项)

设置为YES表示或把这个域中的内容完全存储到文件中,方便进行文本的还原

设置为NO表示把这个域的内容不存储到文件中,但是可以被索引,此时内容无法完全 还原(doc.get是为null)

Field.Index(索引选项)

Index.ANALYZED:进行分词和索引,适用于标题、内容等

Index.NOT_ANALYZED:进行索引,但是不进行分词,如果身份证号,姓名,ID等,适 用于精确搜索

Index.ANALYZED_NOT_NORMS:进行分词但是不存储norms信息,这个norms中包括 了创建索引的时间和权值等信息

Index.NOT_ANALYZED_NOT_NORMS:即不进行分词也不存储norms信息

Index.NO:不进行索引 

最佳实践:

Field.Index

Field.Store

NOT_ANALYZED_NO_NORMS

YES

标识符(主键,文件名),电话号码,身份证号,姓名,日期

ANALYZED

YES

文档摘要和标题

ANALYZED

NO

文档正文

NO

YES

文档类型,数据库主键(不进行索引)

NOT_ANALYZED

NO

隐藏关键字

6. 索引的删除、更新、优化

1.索引的删除

//使用indexWriter删除

2.索引的恢复删除:

3.强制删除:

4.更新(先删除后添加):

7. 当项目中始终只用到一个indexReader和一个indexWriter的时候,也就是把这两个设计 为单例的时候,就不能随手关闭indexReaderindexWriter,但是多线程同时执行搜索 索引的时候就会出现异常信息,这时候lucene提供了解决方案:

即调用IndexReaderopenIfChanged()方法。当然实际项目经常将readerwriter新建 后立即存放到application里保存就可以了。

8.  对数字或日期(当作数字处理)添加索引的时候就不能再简单的使用:

doc.add(new Field("name",names[i],Field.Store.YES,Field.Index.NOT_ANALYZED_NO_NORMS))

了(第二个参数类型是String,不是int),而使用

//存储数字

doc.add(new NumericField("attach",Field.Store.YES,true).setIntValue(attachs[i]));

//存储日期

doc.add(new NumericField("date",Field.Store.YES,true).setLongValue(dates[i].getTime()));

9. 分页:lucene的分页非常撮,没有数据库如hibernate里的limited方法,它是每次加载 所有的数据,然后取出你要显示的那页数据。至此,lucene官网解释是因为我们的检索 速度快得惊人,让你察觉不到,而且这种方法还是比数据库的要快。而到了3.5.0版本, luceneindexsearcher开始加入了一个叫做searchAfter()的方法,它是从上页的最后一 个元素开始检索的,希望在以后的版本中能出来类似hibernatelimited()方法。下面给 出一个效率稍微高一点的分页方法:

10. 分词器:

1.分词过程:

2. Lucene3.5.0Tokenizer的类图结构:

以 how are you ,I'm a student为例,whilespaceTokenizer会分词为[how][are][you,I'm][a][student],而LetterTokenizer会分词为[how][are][you][I][am][a][student]

3. Lucene3.5.0TokenFilter的类图结构:

4. Lucene3.5.0在对一个readerStream分词时需记录的信息(这里最关键了!!!):

5. 用户自扩展分词器,只需集成Analyzer类,重写里面的方法,为它设定过滤器链 即可。如:

6. 中文分词器:庖丁解牛分词器(不再更新词库)、mmseg4j(使用sugou词库,支 持 用户自定义词库)、IK Analyzer。但是,他们都不支持同义词搜索。

7. 扩展写一个自己的支持同义词搜索的中文分词器是很简单的。首先,利用mmseg4j 进行分词,就可以得到正常的分词tokenStream,然后遍历这个tokenStream,判断 当前的token是否有同义词(当然,有一个map集合来存储同义词),这样就支持 同义词的中文搜索了。

11. 高级搜索

1. 搜索排序:

2. 搜索过滤

3. 自定义评分

A.创建一个Query

B.创建一个FieldScoreQuery

C.写一个自定义类继承CustomScoreQuery,实现里面的构造方法和getCustomScoreProvider(),在getCustomScoreProvider()返回一个自定义的CustomScoreProvider对象,见D

D.写一个自定义类继承CustomScoreProvider,实现里面的构造方法和customScore(),在 customScore()编写自己的评分法则

E.利用A,B中的QueryFieldScoreQuery对象创建自定义的CustomScoreQuery对象,用这个query对象来查询,即传给indexSearcher

F.代码实例:

/**

 * 自定义评分

 * @author Administrator

 *

 */

public class MyScoreQuery { 

public void searchByMyScoreQuery(){

try {

IndexReader reader = IndexReader.open(FSDirectory.open(new File("E:\\Project\\MyEclipse9.1\\LuceneLearning\\index")));

IndexSearcher searcher = new IndexSearcher(reader);

Query query = new TermQuery(new Term("content""like"));

//创建一个评分域

FieldScoreQuery fsq = new FieldScoreQuery("score", Type.INT);

//根据评分域和原有的query创建自定义的query对象

MyCustomScoreQuery myCustomScoreQuery = new MyCustomScoreQuery(query, fsq);

TopDocs docs = searcher.search(myCustomScoreQuery, 10);

System.out.println("一共找到的文件数:"+docs.totalHits);

ScoreDoc[] sds = docs.scoreDocs;

Document doc  = null;

for(ScoreDoc sd : sds){

doc = searcher.doc(sd.doc);

System.out.println("ID:"+doc.get("id")+"--"+(Integer.parseInt(doc.get("score")))/(sd.score)+"--NAME:"+doc.get("name")+"--EMAIL:"+doc.get("email")+"--ATTACH:"+doc.get("attach"));

}

searcher.close();

catch (CorruptIndexException e) {

e.printStackTrace();

catch (IOException e) {

e.printStackTrace();

}

}

/**

 * 写一个类继承CustomScoreQuery

 * 实现里面的构造方法和getCustomScoreProvider

 * @author Administrator

 *

 */

private class MyCustomScoreQuery extends CustomScoreQuery{

/**

 * 

 */

private static final long serialVersionUID = -479883456177308726L;

public MyCustomScoreQuery(Query subQuery, ValueSourceQuery valSrcQuery) {

super(subQuery, valSrcQuery);

}

/**

 * 自己获取一个评分

 */

@Override

protected CustomScoreProvider getCustomScoreProvider(IndexReader reader)

throws IOException {

//默认情况下的评分法则是subQuery*valSrcQuery即(原有的评分)*(传入进来的评分域所获取的评分)

//为了写自己的评分法则需要写一个自己的CustomScoreProvider,即下面的MyCustomScoreProvider类

//return super.getCustomScoreProvider(reader);

return new MyCustomScoreProvider(reader);

}

}

/**

 * 写一个类继承CustomScoreProvider

 * 实现里面的构造方法和customScore

 * @author Administrator

 *

 */

private class MyCustomScoreProvider extends CustomScoreProvider{

public MyCustomScoreProvider(IndexReader reader) {

super(reader);

}

@Override

public float customScore(int doc, float subQueryScore, float valSrcScore)

throws IOException {

//return super.customScore(doc, subQueryScore, valSrcScore);

//假设我们的需求是这样的评分

return valSrcScore/subQueryScore;

}

}

}

4. 自定义QueryParser:由于某些QueryParser(:WildCardQueryPrefixQuery FuzzyQuery)的性能低下,所以考虑将其取消。实现思路:

A. 继承QueryParser类,并且重载里面的相应方法,如getWilfCardQuery()

5. 工具介绍:

1. luke(可以解析lucene索引文件,注意版本必须和你的lucene严格一致, 否则打不开)

2. Tika:用于打开各种类型的文件的apache软件,由于我们可能要对一个二进制 文件而不是文本进行索引(比如word),所以就不能直接打开,这时候可以借助tika 提取出里面的文本再索引。使用tika 的方式很简单,有两种:

/**

 * 第一种使用Tika读取文件的方式

 * @param fileName

 * @return

 * @throws TikaException 

 * @throws SAXException 

 * @throws IOException 

 */

public String fileToTxt(String fileName) throws IOException, SAXException, TikaException{

Parser parser = new AutoDetectParser();  //当不知道文件类型时,就创建一个AutoDetectParser,它会自动匹配

InputStream is = null;

is = new FileInputStream(new File(fileName));

ContentHandler handler = new BodyContentHandler();

Metadata metadata = new Metadata();

ParseContext context = new ParseContext();

context.set(Parser.class, parser);

parser.parse(is, handler, metadata, context);

metadata.set(Metadata.AUTHOR"王欢");

for(String name:metadata.names()){

System.out.println(name+":"+metadata.get(name));

}

return handler.toString();

}

/**

 * 第二种使用Tika读取文件的方式

 * @param fileName

 * @return

 * @throws TikaException 

 * @throws IOException 

 */

public String fileToTxt2(String fileName) throws IOException, TikaException{

return new Tika().parseToString(new File(fileName));

}

而建立文件的索引的时候就应该这样来读了:

doc.add(new Field("content", new Tika().parse(new File(fileName))));

如:

/**

 * 利用Tika读取任何类型的文件并生成索引

 * @param fileName

 */

public void index(String fileName){

IndexWriter writer = null;

Directory dir;

try {

dir = FSDirectory.open(new File("D:\\Documents\\Desktop\\新建文件夹\\myIndex"));

writer new IndexWriter(dir, new IndexWriterConfig(Version.LUCENE_35new MMSegAnalyzer()));//使用中文分词器

writer.deleteAll();  //先删除所有的索引

Document doc = new Document();

doc.add(new Field("content"new Tika().parse(new File(fileName))));  //使用Tika去除掉无用的东西

writer.addDocument(doc);

catch (Exception e) {

e.printStackTrace();

}finally{

if(writer != null)

try {

writer.close();

catch (CorruptIndexException e) {

e.printStackTrace();

catch (IOException e) {

e.printStackTrace();

}

}

}

12. Solr:全文搜索服务器(用于管理索引,版本和lucene要严格一致!)

1. Solrtomcat整合

A. 建立一个文件夹目录solr->homesolr->server,将下载的apache-solr-3.5.0.zip里的example下的solr文件夹里的内容复制到home里,将webapps下的solr.war解压到server里;

B. 修改solr\home\conf下的solrConfig.xml文件:

<dataDir>${solr.data.dir:G:\study\lucene\solr\home\data}</dataDir>

<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" enable="${solr.velocity.enabled:false}"/>

C.配置Tomcat 6.0\conf下的server.xml文件,在<HOST>之间增加:

<!--solr-->

<Context path="/solr" docBase="G:\study\lucene\solr\server" reloadable="false">

<Environment name="solr/home" type="java.lang.String" value="G:\study\lucene\solr\home" override="true"> 

</Environment>

</Context>

D.使solr支持中文分词:

1.导包:将下载的mmseg4j-1.8.5.zip里的mmseg4j-all-1.8.5.jarmmseg4j-solr-1.8.5.jar拷贝到   solr\server\WEB-INF\lib里;

2.修改solr\home\conf下的schema.xml文件,在<types>之间增加:(这段代码在mmseg4j-1.8.5.zip\readme.txt里)

<!--MMseg Analyzer-->

<fieldType name="textComplex" class="solr.TextField" >

      <analyzer>

        <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="complex" dicPath="dic"/>

      </analyzer>

    </fieldType>

<fieldType name="textMaxWord" class="solr.TextField" >

      <analyzer>

        <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="max-word" dicPath="dic"/>

      </analyzer>

    </fieldType>

<fieldType name="textSimple" class="solr.TextField" >

      <analyzer>

        <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="simple" dicPath="n:/OpenSource/apache-solr-1.3.0/example/solr/my_dic"/>

      </analyzer>

    </fieldType>

3. 拷贝mmseg4j-1.8.5.zip\data下的几个txt文件到solr\home\dic

2. SolrJava整合

A. 导包(位于apache-solr-3.5.0\dist\solrj-libapache-solr-3.5.0\dist下的):

B. 所有自定义的field都在schema.xml文件里定义,如:

<field name="test_all" type="textComplex" indexed="true" stored="true" multiValued="true"/>

<field name="test_title" type="textComplex" indexed="true" stored="true"/>

<field name="test_content" type="textComplex" indexed="true" stored="true"/>

C.为了加快搜索可定义

<copyField source="test_title" dest="test_all"/>

<copyField source="test_content" dest="test_all"/>

D.需注意的几点:

 <uniqueKey>id</uniqueKey>   (所以每个doc必须要赋予idid是主键)

 <defaultSearchField>text</defaultSearchField>(默认搜索域是text,可以自己更改)

 <solrQueryParser defaultOperator="OR"/>(默认操作符是OR ,也可以自己设定)

E.示范代码:

public class TestSolrUtils {

private static String url = "http://localhost:8080/solr";

/**

 * 第一种方式添加document

 */

@Test

public void test01(){

try {

CommonsHttpSolrServer server = new CommonsHttpSolrServer(url);

server.deleteByQuery("*:*");

server.commit();

SolrInputDocument doc = new SolrInputDocument();

doc.addField("id""1");

doc.addField("test_title""这是我的第一个solr小程序");

doc.addField("test_content""有了solr就是爽啊,哈哈哈~!!!");

server.add(doc);

server.commit();

catch (MalformedURLException e) {

e.printStackTrace();

catch (SolrServerException e) {

e.printStackTrace();

catch (IOException e) {

e.printStackTrace();

}

}

/**

 * 第二种方式添加document

 */

@Test

public void test02(){

CommonsHttpSolrServer server;

try {

server = new CommonsHttpSolrServer(url);

server.deleteByQuery("*:*");

server.commit();

List<Message> msgs = new ArrayList<Message>();

msgs.add(new Message("1""第一个javabean""用javabean的方式添加第一个solr的document!"));

msgs.add(new Message("2""第二个javabean""用javabean的方式添加第二个solr的document!"));

msgs.add(new Message("3""第三个javabean""用javabean的方式添加第三个solr的document!"));

server.addBeans(msgs);

server.commit();

catch (MalformedURLException e) {

e.printStackTrace();

catch (SolrServerException e) {

e.printStackTrace();

catch (IOException e) {

e.printStackTrace();

}

}

/**

 * 使用solr查询索引

 */

@Test

public void test03(){

CommonsHttpSolrServer server;

try {

server = new CommonsHttpSolrServer(url);

SolrQuery query = new SolrQuery();

query.setQuery("test_title:javabean");

/**solr的分页*/

query.setStart(0);

query.setRows(2);

QueryResponse response = server.query(query);

SolrDocumentList sdl = response.getResults();

for(SolrDocument sd : sdl){

System.out.println("id:"+ sd.getFieldValue("id"));

System.out.println("test_title:"+sd.getFieldValue("test_title"));

System.out.println("test_content:"+sd.getFieldValue("test_content"));

System.out.println("***********************************************");

}

catch (MalformedURLException e) {

e.printStackTrace();

catch (SolrServerException e) {

e.printStackTrace();

}

}

/**

 * solr高亮

 */

@Test

public void test04(){

CommonsHttpSolrServer server ;

try {

server = new CommonsHttpSolrServer(url);

SolrQuery query = new SolrQuery();

query.setQuery("test_content:javabean");

query.setHighlight(true).setHighlightSimplePre("<span style='color:red'>")

 .setHighlightSimplePost("</span>").setStart(1).setRows(3);

query.setParam("hl.fl""test_title,test_content");

QueryResponse response = server.query(query);

SolrDocumentList sdl= response.getResults();

for(SolrDocument sd: sdl){

String id = (String)sd.getFieldValue("id");

//采用solr要高亮的部分必须先存储,否则无法高亮,只能采取再查询的方式

System.out.println(response.getHighlighting().get(id).get("test_title"));

System.out.println(response.getHighlighting().get(id).get("test_content"));

}

catch (MalformedURLException e) {

e.printStackTrace();

catch (SolrServerException e) {

e.printStackTrace();

}

}

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值