搜索引擎项目代码主要分为三个部分,第一部分是构建索引,全文检索;第二部分是输入问题,对问题进行分词、提取关键词、关键词扩展;第三部分是将搜索结果输出到GUI图形用户界面。java
搜索引擎的工做流程能够简化归结为如下四个步骤:数据库
(1)网上抓取网页apache
(2)创建索引数据库架构
(3)在索引数据库中搜索ide
(4)对搜索结果进行处理和排序函数
全文检索是一种将文件中全部文本与检索项匹配的文字资料检索方法。性能
全文检索大致分两个过程,索引建立 (Indexing) 和搜索索引 (Search) 。ui
①索引建立:从结构化和非结构化数据提取信息,建立索引的过程。this
②搜索索引:获得用户的查询请求,搜索建立的索引,而后返回结果。搜索引擎
Lucene是一个高性能,易于扩展的IR(Information Retrieval)Java类库,能够利用其中的Java类轻松地在应用程序中增长索引和搜索功能。Lucene彻底用Java实现,具备良好的跨平台性,是Apache Jakarta项目中一个子项目。
Lucene的架构图以下图4.1所示:
图4.1Lucene的架构图
Lucene的核心的索引类简介以下:
① IndexWriter类
IndexWriter是索引过程当中的核心部件。这个类创建一个新的索引和添加文档(Documents)到一个存在的索引中,它对索引只有写权限,不具备读和搜索的权限。
② Directory类
Directory类表明了Lucene索引的位置,它是一个抽象类,有两个子类: FSDirectory类,它将索引存储在磁盘上,并在文件系统中维护一个真实文件的列表;另外一个是RAMDirectory类,它将全部索引信息都放在内存中,适用于索引较小的状况。
③ Analyzer类
在文本被索引以前,要通过一个Analyzer的处理。在IndexWriter的构造函数中指定的Analyzer负责从被索引的文本中抽取出标志(tokens),并排除掉标志之外的部分。假如被索引的内容不是纯文本的话,应该先将它转换为纯文本。Analyzer是一个抽象类,Lucene中包含了几种它的实现,用于完成不一样的功能,例如用于去掉文本中的无用词(stop word)、将标志(tokens)所有转换为小写等等。为了达到特定的目标,能够设计不一样的Analyzer的实现,这样就使得Lucene具备高度的灵活性及可扩充性。
④ Document类
一个Document表明了一些字段(fields)的集合。
⑤ Field类:
在索引中的每一个Document都包含了一个或更多的命名了的字段(fields),这些字段用一个称为Field的类来表示。每一个字段都对应于在搜索期间能从索引中查询并提取的一段数据。
⑥ IndexSearcher类
IndexSearcher用于搜索IndexWriter创建的索引。IndexSearcher以只读方式打开索引,并提供了几种搜索方法,这些方法是它的抽象父类的实现。
⑦ Term类
Term是最基本的搜索单元。与Field对象相似,它也包含了一对字符串元素:名字和值。Term对象和索引过程也是相关的。在搜索期间,先构建Term对象而后连同TermQuery一块儿使用。
⑧ TermQuery类
TermQuery类是Lucene支持的最基本的查询类。它被用来匹配含有特定值的字段的文档。
⑨ Hits类
Htis类是一个简单的指向分级搜索结果的指针容器。考虑到性能的缘由,Hits的实例并不从索引中装载匹配查询的全部文档只是他们的一小部分。
构建索引所用到的数据来自搜狗实验室( www.sogou.com/labs/resource)的搜狐新闻数据,对其中健康(health)和娱乐(yule)两类的数据构建了索引。
①Lucene-core-4.0.0.jar:包括了经常使用的文档,索引,搜索,存储等相关核心代码。
②Lucene-analyzers-common-4.0.0.jar:包含词法分析器,用于提取关键字。
③Lucene-highlighter-4.0.0.jar:将搜索出的内容高亮显示。
④Lucene-queryparser-4.0.0.jar:用于各类搜索,如模糊搜索,范围搜索等。
①建立索引,经过IndexWriter对不一样的文件进行索引的建立,并将其保存在索引相关文件存储的位置中。
②经过索引查询关键字相关文档。
①定义词法分析器
②肯定索引文件存储的位置
③建立IndexWriter,进行索引文件的写入
④内容提取,进行索引的存储
⑤关闭查询器
4.所有代码
package lucene; public class Article { private Integer id; private String title; private String content; public Article() { super(); } public Article(Integer id, String title, String content) { super(); this.id = id; this.title = title; this.content = content; } public synchronized Integer getId() { return id; } public synchronized void setId(Integer id) { this.id = id; } public synchronized String getTitle() { return title; } public synchronized void setTitle(String title) { this.title = title; } public synchronized String getContent() { return content; } public synchronized void setContent(String content) { this.content = content; } @Override public String toString() { return "Article [id=" + id + ", title=" + title + ", content=" + content + "]"; } }
package lucene; /* * 环境:lucene-3.6.0 */ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.Sort; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.store.SimpleFSDirectory; import org.apache.lucene.util.Version; import org.wltea.analyzer.lucene.IKAnalyzer; import lucene.Article; /** * Lucene 检索各类索引的实现方式总结 * @author Administrator * */ public class lucene_Search { //public static final String INDEX_DIR_PATH = "indexDir1"; public static final String INDEX_DIR_PATH = "indexDir"; /* 建立简单中文分析器 建立索引使用的分词器必须和查询时候使用的分词器同样,不然查询不到想要的结果 */ private Analyzer analyzer = null; // 索引保存目录 private File indexFile = null; //目录对象,由于操做索引文件都要用到它,因此定义为全局变量 private Directory directory = null; //索引搜索对象 private IndexSearcher indexSearcher; /** * 初始化方法 * @throws IOException */ public void init() throws IOException{ analyzer = new IKAnalyzer(true); indexFile = new File(INDEX_DIR_PATH); directory = new SimpleFSDirectory(indexFile); indexSearcher = new IndexSearcher(directory); System.out.println("*****************搜索索引程序初始化成功**********************"); } /** * 根据传递的结果集 封装成集合后显示出来 * @param scoreDocs * @return * @throws IOException * @throws CorruptIndexException */ public List<String> showResult(ScoreDoc[] scoreDocs) throws CorruptIndexException, IOException{ List<Article> articles = new ArrayList<Article>(); for (int i = 0; i < scoreDocs.length; i++) { int doc = scoreDocs[i].doc;//索引id Document document = indexSearcher.doc(doc); Article article = new Article(); if (document.get("id") == null) { System.out.println("id为空"); } else { article.setId(Integer.parseInt(document.get("id"))); article.setTitle(document.get("title")); article.setContent(document.get("content")); articles.add(article); } } List<String> resultList = new ArrayList<String>(); if(articles.size()!=0){ for (Article article : articles) { //System.out.println(article); resultList.add(article.getContent()); } }else{ System.out.println("没有查到记录。"); } return resultList; } /** * 经过QueryParser绑定单个字段来检索索引记录 * @param keyword * @return * @throws ParseException * @throws IOException * @throws CorruptIndexException */ public List<String> searchByQueryParser(String keyword) throws ParseException, CorruptIndexException, IOException{ System.out.println("*****************经过QueryParser来检索索引记录**********************"); QueryParser queryParser = new QueryParser(Version.LUCENE_36, "content", analyzer); Query query = queryParser.parse(keyword); // public TopFieldDocs search(Query query, int n, Sort sort) // 参数分别表示 Query查询对象,返回的查询数目,排序对象 TopDocs topDocs = indexSearcher.search(query, 50, new Sort()); List<String> resultList = showResult(topDocs.scoreDocs); return resultList; } /** * 销毁当前的操做类的实现,主要关闭资源的链接 */ public void destory() throws IOException{ analyzer.close(); directory.close(); System.out.println("*****************成功关闭资源链接**********************"); } public static List<String> lucene_searchIndex(String question) throws IOException, ParseException { lucene_Search luceneInstance = new lucene_Search(); luceneInstance.init(); List<String> resultList = luceneInstance.searchByQueryParser(question); luceneInstance.destory(); HashSet<String> resultset = new HashSet<String>(); for(int i=0;i<resultList.size();i++){ resultset.add(resultList.get(i)); } resultList.clear(); for(String data:resultset){ resultList.add(data); } return resultList; } }
package lucene; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.Term; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.store.Directory; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.SimpleFSDirectory; import org.apache.lucene.util.Version; import org.wltea.analyzer.lucene.IKAnalyzer; import Utils.readBigData; public class lucene_BuildIndex { // public static final String INDEX_DIR_PATH = "indexDir1"; public static final String INDEX_DIR_PATH = "indexDir3"; /* 建立简单中文分析器 建立索引使用的分词器必须和查询时候使用的分词器同样,不然查询不到想要的结果 */ private Analyzer analyzer = null; // 索引保存目录 private File indexFile = null; //目录对象,由于操做索引文件都要用到它,因此定义为全局变量 private Directory directory = null; //建立IndexWriter索引写入器 IndexWriterConfig indexWriterConfig = null; SimpleDateFormat simpleDateFormat = null; /** * 得到指定格式的时间字符串 * @return */ public String getDate(){ return simpleDateFormat.format(new Date()); } /** * 初始化方法 * @throws IOException */ public void init() throws IOException{ analyzer = new IKAnalyzer(true); indexFile = new File(INDEX_DIR_PATH); directory = new SimpleFSDirectory(indexFile); simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("*****************倒排索引程序初始化成功**********************"); } /** * 将article对象的属性所有封装成document对象,重构以后方便建立索引 * @param article * @return */ public Document createDocument(Article article){ Document document = new Document(); document.add(new Field("id", article.getId().toString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); document.add(new Field("content", article.getContent().toString(), Field.Store.YES, Field.Index.ANALYZED)); //document.add(new Field("title", article.getTitle().toString(),Field.Store.YES, Field.Index.ANALYZED)); return document; } /** * 为了如实反映操做索引文件以后的效果,每次操做以后查询索引目录下全部的索引内容 * @throws IOException * @throws CorruptIndexException */ public void openIndexFile() throws CorruptIndexException, IOException{ System.out.println("*****************读取索引开始**********************"); IndexReader indexReader = IndexReader.open(directory); int docLength = indexReader.maxDoc(); for (int i = 0; i < docLength; i++) { Document doc = indexReader.document(i); Article article = new Article(); if (doc.get("id") == null) { System.out.println("id为空"); } else { article.setId(Integer.parseInt(doc.get("id"))); article.setTitle(doc.get("title")); article.setContent(doc.get("content")); } //System.out.println(article); } System.out.println("*****************读取索引结束**********************\n"); } /** * 建立索引到索引文件中 * @param article * @throws IOException * @throws LockObtainFailedException * @throws CorruptIndexException */ public void createIndex(Article article) throws CorruptIndexException, LockObtainFailedException, IOException{ indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36, analyzer); IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig); indexWriter.addDocument(createDocument(article)); indexWriter.close(); //System.out.println("[ " + getDate() + " ] Lucene写入索引到 [" + indexFile.getAbsolutePath() + "] 成功。"); } /** * 根据文件中的id删除对应的索引文件 * @param contentId * @throws IOException * @throws ParseException */ public void deleteIndex(String contentId) throws IOException, ParseException{ //判断索引文件目录内容是否有索引,有返回true ,没有返回false if(IndexReader.indexExists(directory)){ indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36, analyzer); IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig); //封装term去检索字段名为id ,具体值为contentId的记录,若是存在就会删除,不然什么都不作 indexWriter.deleteDocuments(new Term("id",contentId)); /* QueryParser queryParser = new QueryParser(Version.LUCENE_36, "id", analyzer); Query query = queryParser.parse(contentId); indexWriter.deleteDocuments(query); */ indexWriter.close(); System.out.println("[ " + getDate() + " ] Lucene删除索引到 [" + indexFile.getAbsolutePath() + "] 成功。"); }else{ throw new IOException("[ " + getDate() + " ] Lucene删除索引失败,在 " + indexFile.getAbsolutePath() + "目录中没有找到索引文件。" ); } } /** * 因为实际的内容修改,因此索引文件也要跟着修改 ,具体实现方式就是先删除索引,而后从新添加 * @param article * @throws IOException * @throws ParseException */ public void updateIndex(Article article) throws IOException, ParseException{ deleteIndex(article.getId().toString()); createIndex(article); System.out.println("updateIndex函数执行完毕"); } /** * 销毁当前的操做类的实现,主要关闭资源的链接 * * @throws IOException */ public void destory() throws IOException{ analyzer.close(); directory.close(); System.out.println("*****************成功关闭资源链接**********************"); } public static void main(String[] args) throws IOException, ParseException { String path = "D:\\#个人文件夹\\天然语言处理\\问答系统\\爬取网页\\1_2文档切分红句子.txt"; List<String> information = readBigData.readTxtByStringBuffer(path); lucene_BuildIndex luceneInstance = new lucene_BuildIndex(); luceneInstance.init();//初始化 for(int i=0;i<information.size();i++){ Article article = new Article(i,null,information.get(i)); luceneInstance.createIndex(article); } luceneInstance.openIndexFile(); luceneInstance.destory();//销毁 } }