赞
踩
基于课程学习内容,实现简单的搜索引擎功能(界面可视化不做要求,可终端输出),要求实现以下基本功能:
这里使用的是lucene-8.0.0,由于版本不同,网上很多博客的教程已经失效,具体的api参数或者调用要参考官网最新的手册,这里需要一定的搜索与查阅文档的能力。
http://lucene.apache.org/core/8_0_0/core/
项目完整源码:Github传送门
下面只讲述部分关键的代码
这里利用IndexWriter类来构建索引,由于这里使用的是中文文档,故要使用分析中文的分析器SmartChineseAnalyzer
.
根据建立索引的目录以及数据的目录来读取。
定义一个fieldType,并设置其属性,既保存在文件又用于索引建立
读取 file 转 string
用文件内容来建立倒排索引
用文件名来建立倒排索引
用文件路径来建立倒排索引
public class Indexer { private IndexWriter writer; public Indexer(String indexDirectoryPath) throws IOException{ // 获取目录directory Directory indexDirectory = FSDirectory.open(FileSystems.getDefault().getPath(indexDirectoryPath)); // 中文分析器 Analyzer analyzer = new SmartChineseAnalyzer(); IndexWriterConfig config = new IndexWriterConfig(analyzer); writer = new IndexWriter(indexDirectory, config); } public void close() throws CorruptIndexException, IOException{ writer.close(); } private Document getDocument(File file) throws IOException{ Document document = new Document(); // 定义一个fieldType,并设置其属性,既保存在文件又用于索引建立 FieldType fieldType = new FieldType(); fieldType.setStored(true); fieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS); // 读取 file 转 string StringBuffer buffer = new StringBuffer(); BufferedReader bf= new BufferedReader(new FileReader(file)); String s = null; while((s = bf.readLine())!=null){//使用readLine方法,一次读一行 buffer.append(s.trim()); } String xml = buffer.toString(); // 用文件内容来建立倒排索引 Field contentField = new Field(LuceneConstants.CONTENTS, xml,fieldType); // 用文件名来建立倒排索引 Field fileNameField = new Field(LuceneConstants.FILE_NAME,file.getName(),fieldType); // 用文件路径来建立倒排索引 Field filePathField = new Field(LuceneConstants.FILE_PATH,file.getCanonicalPath(),fieldType); // 添加到document document.add(contentField); document.add(fileNameField); document.add(filePathField); return document; } private void indexFile(File file) throws IOException{ System.out.println("Indexing "+file.getCanonicalPath()); Document document = getDocument(file); writer.addDocument(document); } public int createIndex(String dataDirPath, FileFilter filter) throws IOException{ //get all files in the data directory File[] files = new File(dataDirPath).listFiles(); int count = 0; for (File file : files) { // System.out.println(file); if(!file.isDirectory() && !file.isHidden() && file.exists() && file.canRead() && filter.accept(file) ){ indexFile(file); count++; } } return count; } }
测试函数:
public class LuceneTester { String indexDir = "C:/Users/asus/Desktop/java/information-retrieval-system/index"; String dataDir = "C:/Users/asus/Desktop/java/information-retrieval-system/data"; Indexer indexer; public static void main(String[] args) { LuceneTester tester; // File[] fs = new File("C:/Users/asus/Desktop/java/information-retrieval-system/data").listFiles(); // for (File f : fs){ // System.out.println(f); // } try { tester = new LuceneTester(); tester.createIndex(); } catch (IOException e) { e.printStackTrace(); } } private void createIndex() throws IOException{ indexer = new Indexer(indexDir); int numIndexed; long startTime = System.currentTimeMillis(); numIndexed = indexer.createIndex(dataDir, new TextFileFilter()); long endTime = System.currentTimeMillis(); indexer.close(); System.out.println(numIndexed+" File indexed, time taken: " +(endTime-startTime)+" ms"); } }
输出结果:这里我们就已经建立好索引,并在文件目录能找到索引文件
文件目录中的索引:
public class ReaderByIndexerTest { public static void search(String indexDir,String q)throws Exception{ //得到读取索引文件的路径 Directory dir=FSDirectory.open(Paths.get(indexDir)); //通过dir得到的路径下的所有的文件 IndexReader reader=DirectoryReader.open(dir); //建立索引查询器 IndexSearcher is=new IndexSearcher(reader); // 设置为TF/IDF 排序 ClassicSimilarity sim = new ClassicSimilarity(); // Implemented as sqrt(freq). // sim.tf(reader.getSumDocFreq(q)); // Implemented as log((docCount+1)/(docFreq+1)) + 1. // sim.idf(reader.getSumDocFreq(q), reader.numDocs()); is.setSimilarity(sim); // 实例化分析器 Analyzer analyzer=new SmartChineseAnalyzer(); // 建立查询解析器 /** * 第一个参数是要查询的字段; * 第二个参数是分析器Analyzer * */ QueryParser parser=new QueryParser("contents", analyzer); // 根据传进来的q查找 Query query=parser.parse(q); // 计算索引开始时间 long start=System.currentTimeMillis(); // 开始查询 /** * 第一个参数是通过传过来的参数来查找得到的query; * 第二个参数是要出查询的行数 * */ TopDocs hits=is.search(query, 10); // 计算索引结束时间 long end=System.currentTimeMillis(); System.out.println("匹配 "+q+" ,总共花费"+(end-start)+"毫秒"+"查询到"+hits.totalHits+"个记录"); //遍历hits.scoreDocs,得到scoreDoc /** * ScoreDoc:得分文档,即得到文档 * scoreDocs:代表的是topDocs这个文档数组 * @throws Exception * */ for(ScoreDoc scoreDoc:hits.scoreDocs){ Document doc=is.doc(scoreDoc.doc); System.out.println(doc.get(LuceneConstants.FILE_PATH)); } //关闭reader reader.close(); }
public static String[] checkWord(String queryWord){ //新索引目录 String spellIndexPath = "C:\\Users\\asus\\Desktop\\java\\information-retrieval-system\\newPath"; //已有索引目录 String oriIndexPath = "C:\\Users\\asus\\Desktop\\java\\information-retrieval-system\\index"; //拼写检查 try { //目录 Directory directory = FSDirectory.open((new File(spellIndexPath)).toPath()); SpellChecker spellChecker = new SpellChecker(directory); // 以下几步用来初始化索引 IndexReader reader = DirectoryReader.open(FSDirectory.open((new File(oriIndexPath)).toPath())); // 利用已有索引 Dictionary dictionary = new LuceneDictionary(reader, LuceneConstants.CONTENTS); IndexWriterConfig config = new IndexWriterConfig(new SmartChineseAnalyzer()); spellChecker.indexDictionary(dictionary, config, true); int numSug = 5; String[] suggestions = spellChecker.suggestSimilar(queryWord, numSug); reader.close(); spellChecker.close(); directory.close(); return suggestions; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }
这里调用之前实现好的基类和工具类,并制作简陋的命令行界面来进行信息检索
//测试 public static void main(String[] args) throws IOException { String indexDir="C:\\Users\\asus\\Desktop\\java\\information-retrieval-system\\index"; // 处理输入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str = null; System.out.println("请输入你要搜索的关键词:"); try { str = br.readLine(); System.out.println(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } // 拼写检查 String temp = str; String[] suggestions = checkWord(str); if (suggestions != null && suggestions.length != 0){ System.out.println("你可能想输入的是:"); for(int i = 0; i < suggestions.length; i++){ System.out.println((i+1) + " : " + suggestions[i]); } System.out.println("请选择上面的一个正确的关键词(输入 1 ~ 5),或继续原词(输入0)进行搜索:"); str = br.readLine(); System.out.println(); if (str != "0"){ str = suggestions[str.charAt(0) - '1']; } else{ str = temp; } } try { search(indexDir,str); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
测试结果:
测试用例1解析:
这里我输入一个错误的关键词美利坚共和国,试图进行搜索,然后系统马上就会告诉我拼写检查的结果让我重新选择。
重新选择后会输出美利坚合众国的正确查询结果
测试用例2解析:
这里直接输入一个正确的存在的关键词,就会直接输出美利坚合众国的正确查询结果,不会出现拼写检查的提醒
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。