几百行代码真的可以完成百度搜索引擎吗?转载本文请联系Java极客技术公众号。大家好,我是鸭血迷,大家叫我阿粉。搜索引擎一定不能被大家默认。我们项目中经常用到的ElasticSearch是一个搜索引擎,在我们的日志系统中是必不可少的。ELK整体来说,基本上是运维的标准配置。另外,现在的搜索引擎底层都是基于Lucene的。阿芬最近遇到一个需求,因为数据量没有达到使用ElasticSearch需要的程度,又不想单独部署集群,所以打算基于Lucene实现一个简单的搜索服务。一起来看看吧。背景**Lucene**是一个用于全文检索和搜索的开源程序库,由Apache软件基金会支持和提供。Lucene为全文索引和搜索提供了一个简单而强大的API。Lucene是当今最流行的免费Java信息??检索库。以上解释来自维基百科。我们只需要知道Lucene可以进行全文索引和搜索。这里的索引是一个动词,意思是我们可以对文档、文章或文件等数据进行索引和记录。建立索引后,我们快速查询起来。索引这个词有时候是动词,表示我们要对数据进行索引,有时候是名词,需要根据上下文来判断。新华字典前面的拼音或者书前面的目录,本质上就是索引。访问和导入依赖首先,我们创建一个SpringBoot项目,然后在pom文件中添加如下内容。我这里使用的lucene版本是7.2.1,7??.2.1org.apache.lucenelucene-core${lucene.version}org.apache.lucenelucene-queryparser${lucene.version}org.apache.lucenelucene-analyzers-common${lucene.version}索引数据在使用Lucene之前,我们需要先索引一些文件,然后通过关键字查询。下面来模拟一下整个过程。这里为了方便模拟一些数据,正常的数据应该从数据库或者文件中加载。我们的思路是:生成多个实体数据;将实体数据映射成Lucene文档形式;索引文件;根据关键字查询文档;第一步创建实体如下:importlombok.Data;@DatapublicclassArticleModel{privateStringtitle;privateStringauthor;privateStringcontent;}然后我们写一个工具类来索引数据,代码如下:importorg.apache.commons.collections。CollectionUtils;importorg.apache.commons.lang.StringUtils;importorg.apache.lucene.analysis.Analyzer;importorg.apache.lucene.analysis.standard.StandardAnalyzer;importorg.apache.lucene.document.*;importorg.apache.lucene。index.IndexWriter;importorg.apache.lucene.index.IndexWriterConfig;importorg.apache.lucene.store.Directory;importorg.apache.lucene.store.FSDirectory;importorg.springframework.beans.factory.annotation.Value;importorg.springframework。stereotype.Component;importjava.io.IOException;importjava.nio.file.Paths;importjava.util.ArrayList;importjava.util.List;importjava.util.Map;publicclassLuceneIndexUtil{privatestaticStringINDEX_PATH="/opt/lucene/demo";privatestaticIndexWriterwriter;publicstaticLuceneIndexUtilgetInstance(){returnSingletonHolder.luceneUtil;}privatestaticclassSingletonHolder{publicfinalstaticLuceneIndexUtilluceneUtil=newLuceneIndexUtil();}privateLuceneIndexUtil(){this.initLuceneUtil();}privatevoidinitLuceneUtil(){try{Directorydirect.getdir(FSPathDirectory.getdir(FSPathINDEX_PATH));Analyzeranalyzer=newStandardAnalyzer();IndexWriterConfigiwc=newIndexWriterConfig(analyzer);writer=newIndexWriter(dir,iwc);}catch(IOExceptione){log.error("createluceneUtilerror");if(null!=writer){try{writer.close();}catch(IOExceptionioException){ioException.printStackTrace();}finally{writer=null;}}}}/***索引单个文档**@paramdoc文档信息*@throwsIOExceptionIOException*/publicvoidaddDoc(Documentdoc)throwsIOException{if(null!=doc){writer.addDocument(doc);writer.commit();writer.close();}}/***索引单个实体**@parammodel单个实体*@throwsIOExceptionIO异常*/publicvoidaddModelDoc(Objectmodel)throwsIOException{Documentdocument=newDocument();列表fields=luceneField(model.getClass());fields.forEach(document::add);writer.addDocument(document);writer.commit();writer.close();}/***索引实体列表**@paramobjects实例列表*@throwsIOExceptionIO异常*/publicvoidaddModelDocs(List>objects)throwsIOException{if(CollectionUtils.isNotEmpty(objects)){Listdocs=newArrayList<>();objects.forEach(o->{Documentdocument=newDocument();Listfields=luceneField(o);fields.forEach(document::add);docs.add(document);});writer.addDocuments(docs);}}/***清除所有文档**@throwsIOExceptionIOException*/publicvoiddelAllDocs()throwsIOException{writer.deleteAll();}/***索引文档列表**@paramdocsDocumentList*@throwsIOExceptionIOException*/publicvoidaddDocs(Listdocs)throwsIOException{if(CollectionUtils.isNotEmpty(docs)){longstartTime=System.currentTimeMillis();writer.addDocuments(docs);writer.commit();log.info("总索引{}个文档,总耗时{}毫秒",docs.size(),(System.currentTimeMillis()-startTime));}else{log.warn("索引列表为空");}}/***根据实体类对象获取字段类型,并进行luceneField字段映射**@parammodelObjEntitymodelObj对象*@return字段映射列表*/publicListluceneField(ObjectmodelObj){MapclassFields=ReflectionUtils.getClassFields(modelObj.getClass());MapclassFieldsValues=ReflectionUtils.getClassFieldsValues(modelObj);Listfields=newArrayList<>();for(Stringkey:classFields.keySet()){Fieldfield;StringdataType=StringUtils.substringAfterLast(classFields.get(key).toString(),".");switch(dataType){case"Integer":field=newIntPoint(键,(整数)classFieldsValues.get(键));中断;案例“长”:field=newLongPoint(键,(长)classFieldsValues.get(键));中断;案例“浮动”:字段=newFloatPoint(键,(Float)classFieldsValues.get(key));break;case"Double":field=newDoublePoint(key,(Double)classFieldsValues.get(key));break;case"String":Stringstring=(String)classFieldsValues.get(键);如果(圣ringUtils.isNotBlank(string)){if(string.length()<=1024){field=newStringField(key,(String)classFieldsValues.get(key),Field.Store.YES);}else{field=newTextField(键,(字符串)classFieldsValues.get(键),Field.Store.NO);}}else{field=newStringField(键,StringUtils.EMPTY,Field.Store.NO);}break;默认:field=newTextField(键,JsonUtils.obj2Json(classFieldsValues.get(key)),Field.Store.YES);break;}fields.add(field);}returnfields;}publicvoidclose(){if(null!=writer){try{writer.close();}catch(IOExceptione){log.error("closewritererror");}writer=null;}}publicvoidcommit()throwsIOException{if(null!=writer){writer.commit();writer.close();}}}有了工具类,我们再写一个demo来进行数据的搜索importjava.util.ArrayList;importjava.util.List;/***
*函数:
*作者:@authorSilence
*日期:2020-10-1721:08
*作者:无
*/publicclassDemo{publicstaticvoidmain(String[]args){LuceneIndexUtilluceneUtil=LuceneIndexUtil.getInstance();Listarticles=newArrayList<>();try{//索引数据ArticleModelarticle1=newArticleModel();article1.setTitle("Java极客技术");article1.setAuthor("鸭血fans");article1.setContent("这是一篇向大家介绍Lucene的技术文章,一定要点赞、评论和转发!!!");ArticleModelarticle2=newArticleModel();article2.setTitle("极客技术");article2.setAuthor("鸭血粉");article2.setContent("此处省略两千字...");ArticleModelarticle3=newArticleModel();article3.setTitle("Java极客技术");article3.setAuthor("鸭血粉丝");article3.setContent("终于邀请你加入我们的知识星球,Todayisbigday!");articles.add(article1);articles.add(article2);articles.add(article3);luceneUtil.addModelDocs(articles);luceneUtil.commit();}catch(Exceptione){e.printStackTrace();}}}以上内容内容可以自己替换,阿粉就不贴了,以免有凑数的嫌疑的话。显示运行结束之后,我们使用Lucene的可视化工具luke查看索引数据内容。下载解压后,我们可以看到有.bat和.sh两个脚本。只需根据您自己的系统运行它们即可。阿芬这里在mac上使用sh脚本运行,运行后打开设置的index目录即可。进入后,我们可以看到如下图所示的内容。选择内容,点击showtopitems可以在右侧看到索引数据。这里,根据分词器的不同,索引结果是不同的。这里阿粉使用的tokenizer是标准的分词器,小伙伴们可以根据自己的需求选择适合自己的分词器。搜索数据数据已成功编入索引。接下来,我们需要根据条件搜索数据。我们创建一个LuceneSearchUtil.java来操作数据。importorg.apache.commons.collections.MapUtils;importorg.apache.lucene.analysis.Analyzer;importorg.apache.lucene.analysis.standard.StandardAnalyzer;importorg.apache.lucene.index.DirectoryReader;importorg.apache.lucene.queryparser。classic.QueryParser;importorg.apache.lucene.search.*;importorg.apache.lucene.store.Directory;importorg.apache.lucene.store.FSDirectory;importorg.springframework.beans.factory.annotation.Value;importjava.io。IOException;importjava.nio.file.Paths;importjava.util.Map;publicclassLuceneSearchUtil{privatestaticStringINDEX_PATH="/opt/lucene/demo";privatestaticIndexSearchersearcher;publicstaticLuceneSearchUtilgetInstance(){returnLuceneSearchUtil.SingletonHolder.searchUtil;}privatestaticclassSingletonHolder{publicfinalarchUtilLucene();}privateLuceneSearchUtil(){this.initSearcher();}privatevoidinitSearcher(){Directorydirectory;try{directory=FSDirectory.open(Paths.get(INDEX_PATH));DirecstoryReaderreader=DirectoryReader.open(directory);searcher=newIndexSearcher(reader);}catch(IOExceptione){e.printStackTrace();}}publicTopDocssearchByMap(MapqueryMap)throwsException{if(null==searcher){this.initSearcher();}if(MapUtils.isNotEmpty(queryMap)){BooleanQuery.Builderbuilder=newBool??eanQuery.Builder();queryMap.forEach((key,value)->{if(valueinstanceofString){QueryqueryString=newPhraseQuery(key,(String)value);//QueryqueryString=newTermQuery(newTerm(key,(String)value));builder.add(queryString,BooleanClause.Occur.MUST);}});returnsearcher.search(builder.build(),10);}returnnull;}}在demo.java中添加查询代码如下://查询数据Mapmap=newHashMap<>();map.put("title","Java极客技术");//map.put("标题","极客技术");//map.put("内容","最");LuceneSearchUtilsearchUtil=LuceneSearchUtil.getInstance();TopDocstopDocs=searchUtil.searchByMap(map);System.out.println(topDocs.totalHiTS);运行结果如下,表示查询到两条记录。通过可视化工具我们可以看到标题为“Java极客技术”的记录确实有两条,同时我们也确认只插入了两条记录。注意这里如果根据其他字符查询,可能查不到,因为这里的分词器使用的是默认的分词器,大家可以根据自己的情况使用对应的分词器。至此我们可以对数据进行索引和搜索,但这仍然是一个简单的入门操作。针对不同类型的字段,我们需要使用不同的查询方式,根据系统的特点,需要使用特定的tokenizer。默认的standardtokenizer不是它必须适合我们的使用场景。而且,我们在索引数据的时候,还需要根据字段类型进行不同的Field设置。以上案例只是一个demo,不能用于生产。搜索引擎是互联网行业的领导者,许多先进的互联网技术都是从搜索引擎发展而来的。