java搜索引擎有很多,比较熟悉的就是slor和lucene。

luncene:

概念:全文检索是计算机程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置。当用户查询时根据建立的索引查找,类似于通过字典的检索字表查字的过程

luncene入门:

全文检索(Full-Text Retrieval)是指以文本作为检索对象,找出含有指定词汇的文本。全面、准确和快速是衡量全文检索系统的关键指标。 关于全文检索,我们要知道:

1,只处理文本。

2,不处理语义。

3,搜索时英文不区分大小写。

4,结果列表有相关度排序。 在信息检索工具中,全文检索是最具通用性和实用性的。

全文检索应用场景:

我们使用Lucene,主要是做站内搜索,即对一个系统内的资源进行搜索。如BBS、BLOG中的文章搜索,网上商店中的商品搜索等。使用Lucene的项目有Eclipse、Jira等。一般不做互联网中资源的搜索,因为不易获取与管理海量资源(专业搜索方向的公司除外)。

全文检索不同于数据库检索

全文检索不同于数据库的SQL查询。(他们所解决的问题不一样,解决的方案也不一样,所以不应进行对比)。在数据库中的搜索就是使用SQL,如:SELECT * FROM t WHERE content like ‘%ant%’。这样会有如下问题:

1、匹配效果:如搜索ant会搜索出planting。这样就会搜出很多无关的信息。

2、相关度排序:查出的结果没有相关度排序,不知道我想要的结果在哪一页。我们在使用百度搜索时,一般不需要翻页,为什么?因为百度做了相关度排序:为每一条结果打一个分数,这条结果越符合搜索条件,得分就越高,叫做相关度得分,结果列表会按照这个分数由高到低排列,所以第1页的结果就是我们最想要的结果。

3、全文检索的速度大大快于SQL的like搜索的速度。这是因为查询方式不同造成的,以查字典举例:数据库的like就是一页一页的翻,一行一行的找,而全文检索是先查目录,得到结果所在的页码,再直接翻到这一页。

准备Lucene的开发环境:

搭建Lucene的开发环境只需要加入Lucene的Jar包,要加入的jar包至少要有:

lucene-core-3.0.1.jar(核心包)

contrib\analyzers\common\lucene-analyzers-3.0.1.jar(分词器)

contrib\highlighter\lucene-highlighter-3.0.1.jar(高亮)

contrib\memory\lucene-memory-3.0.1.jar(高亮)

全文检索程序工作流程

操作索引库的方法

下面是一个luncene的hello world

public class Article {
private Integer id; //id
private String title; //标题
private String content; //内容
}

建立索引库

@Test
public void testCreateIndex() throws Exception {
Article article = new Article();
article.setId(1); // 通过设置id的值模拟一个已保存到数据库中的数据
article.setTitle("Lucene是全文检索框架");
article.setContent("全文检索(Full-Text Retrieval)是指以文本作为检索对象,找出含有指定词汇的文本。"); // 建立索引
Directory directory = FSDirectory.open(new File("./indexDir/")); // 索引库目录
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30); // 分词器 // >> 把Aritcle转为Document
Document doc = new Document();
doc.add(new Field("id", article.getId().toString(), Store.YES, Index.NOT_ANALYZED)); // 要把id转为String型
doc.add(new Field("title", article.getTitle(), Store.NO, Index.ANALYZED));
doc.add(new Field("content", article.getContent(), Store.YES, Index.ANALYZED)); // >> 保存到索引库中
IndexWriter indexWriter = new IndexWriter(directory, analyzer, MaxFieldLength.LIMITED);//MaxFieldLength.LIMITED:表示限定分词,不超过10000
indexWriter.addDocument(doc);
indexWriter.close();
}
// 搜索
@Test
public void testSearch() throws Exception {
// 搜索条件
String queryString = "lucene";
Directory directory = FSDirectory.open(new File("./indexDir/")); // 索引库目录
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30); // 分词器 // 1,把查询字符串转为Query对象(只在title中查询)
QueryParser queryParser = new QueryParser(Version.LUCENE_30, "title", analyzer);
Query query = queryParser.parse(queryString); // 2,执行搜索,得到中间结果
IndexSearcher indexSearcher = new IndexSearcher(directory);
TopDocs topDocs = indexSearcher.search(query, 100); //100 返回查询出来的前n条结果 int count = topDocs.totalHits; // 总结果数
ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 前n条结果的信息 }
@Test
public void testSearch() throws Exception {
// 3,处理结果
List<Article> list = new ArrayList<Article>();
for (int i = 0; i < scoreDocs.length; i++) {
ScoreDoc scoreDoc = scoreDocs[i];
System.out.println("得分:" + scoreDoc.score); // 根据内部编号取出真正的Document数据
int docId = scoreDoc.doc;
System.out.println("docId内部编号="+docId);
Document doc = indexSearcher.doc(docId); // 把Document转为Article
Article article = new Article();
article.setId(Integer.parseInt(doc.get("id"))); // 要转型
article.setTitle(doc.get("title"));
article.setContent(doc.get("content")); // doc.getField("content").stringValue()
list.add(article);
}
indexSearcher.close(); // 显示结果
System.out.println("总结果数:" + count);
for (Article article : list) {
System.out.println("---------> id = " + article.getId());
System.out.println("title = " + article.getTitle());
System.out.println("content= " + article.getContent());
}
}

索引库内部结构分析(创建)

1、把原始数据内容存储到数据的缓存区,会自动生成内部编号

2、在更新目录,会使用分词器

Store参数:

YES:存储本字段的原始值

NO:不存储本字段的原始值,这时,获取到信息字段为null

Index参数:

NO:不更新目录

ANALYZED:把本字段的值分词后更新到目录

NOT_ANALYZED不分词,而是把本字段的值当成一个词更新到目录

注意:一般情况下数据的唯一标识符使用不分词索引 如ID,Path,URL或是姓名、日期、数字

Store:能不能按照这个字段搜索到这个结果 Index:如果不更新目录,则不能按照本字段搜索到结果

lucene索引库操作CRUD

保持索引库和数据库状态一致

所有的数据(对象),我们都要存到数据库中。对于要进行搜索的数据,还要存到索引库中,以供搜索。一份数据同时存到数据库与索引库中(格式不同),就要想办法保证他们的状态一致。否则,就会影响搜索结果。

Article和Document之间的转换

/**
* 把Article转为Document
*/
public static Document articleToDocument(Article article) {
Document doc = new Document();
String idStr = article.getId().toString(); // 把Integer转为String型
doc.add(new Field("id", idStr, Store.YES, Index.NOT_ANALYZED));
doc.add(new Field("title", article.getTitle(), Store.YES, Index.ANALYZED));
doc.add(new Field("content", article.getContent(), Store.YES, Index.ANALYZED));
return doc;
}
/**
* 把Document转为Article
*/
public static Article documentToArticle(Document doc) {
Article article = new Article();
Integer id = Integer.parseInt(doc.get("id")); // 把String型转为Integer型
article.setId(id);
article.setTitle(doc.get("title"));
article.setContent(doc.get("content"));
return article;
}
public class Configuration {
private static Directory directory;
private static Analyzer analyzer;
static {
// 初始化所有配置,应通过读取配置文件得到配置的值
try {
directory = FSDirectory.open(new File("./indexDir/"));
analyzer = new StandardAnalyzer(Version.LUCENE_30);
} catch (Exception e) {
throw new RuntimeException(e);
}
} public static Directory getDirectory() {
return directory;
} public static Analyzer getAnalyzer() {
return analyzer;
}
}
public void save(Article article) {
// 1,把对象转为Document
Document doc = ArticleDocumentUtils.articleToDocument(article);
IndexWriter indexWriter = null;
// 2,添加到索引库中
try {
indexWriter = new IndexWriter(Configuration.getDirectory(),Configuration.getAnalyzer(),MaxFieldLength.LIMITED);
indexWriter.addDocument(doc);
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
try {
if(indexWriter!=null){
indexWriter.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
} }
public void delete(Integer id) {
IndexWriter indexWriter = null;
try {
//term:某个字段的一个关键词
Term term = new Term("id", id.toString());
indexWriter = new IndexWriter(Configuration.getDirectory(),Configuration.getAnalyzer(),MaxFieldLength.LIMITED);
indexWriter.deleteDocuments(term);
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
try {
if(indexWriter!=null){
indexWriter.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public void update(Article article) {
IndexWriter indexWriter = null;
try {
//term:某个字段的一个关键词
Term term = new Term("id", article.getId().toString());
Document doc = ArticleDocumentUtils.articleToDocument(article);
indexWriter = new IndexWriter(Configuration.getDirectory(),Configuration.getAnalyzer(),MaxFieldLength.LIMITED);
//indexWriter.updateDocument(term, doc);
// 更新就是先删除再创建
indexWriter.deleteDocuments(term);
indexWriter.addDocument(doc);
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
try {
if(indexWriter!=null){
indexWriter.close();
}
} catch (Exception e) {
Throw new RuntimeException(e);
}
}
}

更新索引库

索引文件的检索与维护,更新是先删除后创建

维护倒排索引有三个操作:添加、删除和更新文档。但是更新操作需要较高的代价。因为文档修改后(即使是很小的修改),就可能会造成文档中的很多的关键词的位置都发生了变化,这就需要频繁的读取和修改记录,这种代价是相当高的。因此,一般不进行真正的更新操作,而是使用“先删除,再创建”的方式代替更新操作。

public SearchResult<Article> search(String queryString, int firstResult, int maxResults) {
IndexSearcher indexSearcher = null;
try {
// 1,把查询字符串转为Query对象
// QueryParser queryParser = new QueryParser(Version.LUCENE_30, "title",Configuration.getAnalyzer()); // 默认只在title中查询
QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[] { "title", "content" }, Configuration.getAnalyzer()); // 默认在多个字段中查询
Query query = queryParser.parse(queryString);
// 2,执行查询,得到中间结果
indexSearcher = new IndexSearcher(Configuration.getDirectory());
TopDocs topDocs = indexSearcher.search(query, firstResult + maxResults); // 最多返回前n条结果
int count = topDocs.totalHits; // 总记录数
ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 前n条结果的信息
// 3,处理结果
List<Article> list = new ArrayList<Article>();
int endIndex = Math.min(firstResult + maxResults, scoreDocs.length); for (int i = firstResult; i < endIndex; i++) { // 只取一段数据
// 根据内部编号取出Document数据
Document doc = indexSearcher.doc(scoreDocs[i].doc);
// 把Document转为Article
Article article = ArticleDocumentUtils.documentToArticle(doc);
list.add(article);
}
return new SearchResult<Article>(count,list); // 返回
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
if(indexSearcher!=null){
try {
indexSearcher.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public class SearchResult<T> {
private int count;
private List<T> list; public SearchResult(int count, List<T> list) {
this.count = count;
this.list = list;
}
//提供set和get方法
}
private ArticleIndexDao1 indexDao1 = new ArticleIndexDao1();
@Test
public void testSave_1() {
// 模拟了一个已保存到数据库中的数据
Article article = new Article();
article.setId(1);
article.setTitle("Lucene全文检索的说明");
article.setContent("全文检索(Full-Text Retrieval)是指以文本作为检索对象,找出含有指定词汇的文本。");
indexDao1.save(article);
}
@Test
public void testSave_25() {
for(int i=1;i<=25;i++){
// 模拟了一个已保存到数据库中的数据
Article article = new Article();
article.setId(i);
article.setTitle("Lucene全文检索的说明");
article.setContent("全文检索(Full-Text Retrieval)是指以文本作为检索对象,找出含有指定词汇的文本。");
indexDao1.save(article);
}
}
private ArticleIndexDao1 indexDao1 = new ArticleIndexDao1();
@Test
public void testDelete() {
Integer id = 1;
indexDao1.delete(id);
} @Test
public void testUpdate() {
// 模拟了一个已保存到数据库中的数据
Article article = new Article();
article.setId(1);
article.setTitle("Lucene全文检索的说明123");
article.setContent("这是更新后的数据123");
indexDao1.update(article);
}
private ArticleIndexDao1 indexDao1 = new ArticleIndexDao1();
@Test
public void testSearch() {
String queryString = "lucene";
//SearchResult<Article> queryResult = indexDao1.search(queryString, 0, 10000);
//SearchResult<Article> queryResult = indexDao1.search(queryString, 0, 10);
//SearchResult<Article> queryResult = indexDao1.search(queryString, 10, 10);
SearchResult<Article> queryResult = indexDao1.search(queryString, 20, 10);
System.out.println("总记录数是:"+ queryResult.getCount());
for(Article article: queryResult.getList()){
System.out.println("-----------> id = " + article.getId());
System.out.println("title = " + article.getTitle());
System.out.println("content= " + article.getContent());
}
}
IndexWriter indexWriter = new IndexWriter(Configuration.getDirectory(),Configuration.getAnalyzer(),MaxFieldLength.LIMITED);
//indexWriter.close(); IndexWriter indexWriter2 = new IndexWriter(Configuration.getDirectory(),Configuration.getAnalyzer(),MaxFieldLength.LIMITED);
//indexWriter2.close();

java所搜引擎slor学习笔记(一)的更多相关文章

  1. 开源规则引擎 Drools 学习笔记 之 -- 1 cannot be cast to org.drools.compiler.kie.builder.impl.InternalKieModule

    直接进入正题 我们在使用开源规则引擎 Drools 的时候, 启动的时候可能会抛出如下异常: Caused by: java.lang.ClassCastException: cn.com.cheng ...

  2. Java程序猿的JavaScript学习笔记(汇总文件夹)

    最终完结了,历时半个月. 内容包含: JavaScript面向对象特性分析,JavaScript高手必经之路. jQuery源代码级解析. jQuery EasyUI源代码级解析. Java程序猿的J ...

  3. Java程序猿的JavaScript学习笔记(8——jQuery选择器)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  4. Java程序猿的JavaScript学习笔记(3——this/call/apply)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  5. Java程序猿的JavaScript学习笔记(1——理念)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  6. Java程序猿的JavaScript学习笔记(10—— jQuery-在“类”层面扩展)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  7. Java程序猿的JavaScript学习笔记(12——jQuery-扩展选择器)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  8. Java程序猿的JavaScript学习笔记(9—— jQuery工具方法)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  9. Java程序猿的JavaScript学习笔记(6——面向对象模拟)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

随机推荐

  1. 【Java并发编程】之十一:线程间通信中notify通知的遗漏

    notify通知的遗漏很容易理解,即threadA还没开始wait的时候,threadB已经notify了,这样,threadB通知是没有任何响应的,当threadB退出synchronized代码块 ...

  2. DAY7-Flask项目

    1.cookie用于用户登录: Flask用于登录的插件:flask-login 插件初始化: 使用插件:  2.访问权限: 访问某个页面要登录之后才能访问: 为插件编写函数,写在user模块中:

  3. STL 算法介绍

    STL 算法介绍 算法概述 算法部分主要由头文件<algorithm>,<numeric>和<functional>组成.        <algorithm ...

  4. In Place Algorithm

    本篇是in place algorithm的学习笔记.目前学习的是in place merge与in place martrix transposition这两个算法. 1.in place merg ...

  5. 安装MySql出现Error Nr.1045的解决办法

    如图,最后一步出现这个错误框 这是因为上次安装过MySql,其用户数据在卸载的时候没有被删除掉,解决办法如下: 显示隐藏的文件夹,打开C盘,找到下图文件删除之 删除之后再安装一遍MySQL,就可以了

  6. 基于excel导入数据到ms sql server

    OLE DB (Object Linking and Embedding, Database, sometimes written as OLEDB or OLE-DB) an API designe ...

  7. 解题:CF570D Tree Requests

    题面 DSU on tree确实很厉害,然后这变成了一道裸题(逃 还是稍微说一下流程吧,虽然我那个模板汇总里写过 DSU on tree可以以$O(n\log n)$的复杂度解决树上子树统计问题,它这 ...

  8. HEOI 2017 游记

    HEOI2017也算是落下帷幕了,那就写一篇 流水账 游记好了. DAY 0 又是熟悉的大学,又是熟悉的机房 YD宾馆的房间依旧破的不行. 晚上在房间颓颓颓....=.= DAY 1 上午去试机,唯一 ...

  9. loj2542「PKUWC2018」随机游走

    题目描述 给定一棵 nn 个结点的树,你从点 xx 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 QQ 次询问,每次询问给定一个集合 SS,求如果从 xx 出发一直随机游走,直到点集 SS ...

  10. (转)编码规范系列(一):Eclipse Code Templates设置

    背景:长久以来,对java编程中的注释不甚理解.再次学习<疯狂JAVA讲义>基础,深深的感到自己基本功的不牢固.所以要做到事无巨细,好好修炼. 认识注释 常识 注释的作用: 回顾原有的代码 ...