Solr教程-安装运行

Solr是一个分布式高性能全文搜索引擎,是基于Lucene开发的非常流行的企业搜索引擎平台。Solr提供了非常丰富的搜索功能,可以用来构建非常复杂的搜索业务。 这个系列教程旨在介绍Solr的常用功能。 准备 在开始之前你需要准备一些必要的硬件及软件设施: JDK 1.8 (或者更高版本) Solr 7.5.x Postman (可选) CentOS 7.x (本系列以此OS为准) VirtualBox (可选) CentOS可以选择安装,Solr是支持多操作系统的,但是在Linux下体验更好,大部分生产环境也都是Linux系统,建议使用CentOS来学习以及使用Solr。 VirtualBox可以选择安装,如果你是Windows操作系统,建议使用VirtualBox安装CentOS来学习使用Solr。 后面的操作都是基于CentOS 7.x版本操作系统来运行。 安装Solr 安装Solr需要Java 1.8或者更高版本,可以使用java -version命令检查你系统上安装的Java版本。由于Java语言有好几个实现,有些实现并不能非常好的支持Solr,根据Lucene JavaBugs来检查你的Java是否对Solr有很好的支持。Solr支持的操作系统包括Linux, MacOS, Windows。 到https://lucene.apache.org/solr/mirrors-solr-latest-redir.html下载对应操作系统的7.5.0版本的Solr安装包。 solr-7.5.0.tgz 是针对Linux/Unix/MacOS系统的安装包 solr-7.5.0.zip 是针对Windows系统的安装包 solr-7.5.0-src.tgz 是源码包 tar zxf solr-7.5.0.tgz cd solr-7.5.0 bin/solr start 然后使用浏览器打开http://localhost:8983,你就可以看到Solr Admin UI的界面了,这是Solr自带的管理界面,使用非常方便,在里面可以查看状态,管理core,进行查询。 以bin/solr start运行的solr是以standalone模式(非分布式)运行的,如果是学习可以使用此模式快速的尝试。但是建议使用后面将要介绍的SolrCloud模式来运行,SolrCloud模式可以很好的进行扩展,应对海量数据的实时搜索,动态剔除/增加机器节点,动态转移数据分片(shard),所有这些操作都可以不停机进行操作。生产环境也建议使用SolrCloud模式运行。 这是以SolrCloud模式启动solr的命令,只需要添加一个-c参数即可 bin/solr start -c 详细的命令行参数请参考Solr控制脚本参考 SolrCloud启动之后会占用8983端口,默认会启动一个内置的zookeeper实例,占用9983端口(在solr的端口上加1000)。 多节点的SolrCloud安装-生产环境部署 一般生产环境部署会把zookeeper单独部署,其他的solr节点单独部署。 外部Zookeeper 下面假设是生产环境部署zookeeper,建议至少需要三个zookeeper节点。 开发环境以及测试使用一个zookeeper也没有关系。 假设已经有三个机器来运行zookeeper来组建一个小的zookeeper集群: 192.168.1.2 192.168.1.3 192.168.1.4 步骤为:下载zookeeper-3.4.13 -> 解压 -> 配置conf/zoo.cfg -> 启动 curl -o zookeeper-3.4.13.tar.gz 'https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.13/zookeeper-3.4.13.tar.gz' tar -xzvf zookeeper-3.4.13.tar.gz cd zookeeper-3.4.13 cp conf/zoo_sample.cfg conf/zoo.cfg sed -i 's/dataDir=.*/dataDir=.\/data/g' conf/zoo.cfg mkdir data echo -e "\nserver.1=192.168.1.2:2888:3888\nserver.2=192.168.1.3:2888:3888\nserver.3=192.168.1.4:2888:3888" >> conf/zoo.cfg echo 1 > data/myid 解释一下上面的一些命令: ...

March 5, 2019

自定义Lucene的分词器Analyzer

0、先定义一下分词的结果 从最简单的做起,把输入的内容按一个字一个字进行切分。这里只是为了说明自定义的Analyzer以及Tokenizer如何写。Analyzer需要一个Tokenizer来进行切词,所以还需要定义一个自己的Tokenizer。 1、先从Analyzer开始 Lucene版本为5.5以上 定义自己的Analyzer需要继承org.apache.lucene.analysis.Analyzer,并且实现TokenStreamComponents createComponents(String s)方法。咱们的Analyzer名字叫DemoAnalyzer。上代码: import org.apache.lucene.analysis.Analyzer; public class DemoAnalyzer extends Analyzer { @Override protected TokenStreamComponents createComponents(String s) { return new TokenStreamComponents( new DemoTokenizer() ); } } 在createComponents方法里面直接new一个TokenStreamComponents对象,对象里面包含的是自定义的DemoTokenizer对象,主要的分词工作就是在这个类里面完成。 2、定义Tokenizer 自定义的Tokenizer需要继承org.apache.lucene.analysis.Tokenizer,并且实现boolean incrementToken()方法。这个方法比较特殊: 此方法并不是一次性调用,而是进行的迭代调用,也就是说一次是读取一个分词,第二次再读取下一个分词,依次类推。 需要一系列的Attribute来存放对应的一个分词(也就是term)的信息,包括词本身、类型(type),位置,词的始末偏移量等等。这个是通过调用addAttribute方法实现的。 input这个Reader并不需要自己传入,这个就是输入的需要分词的文本在父类已经实现了setReader(Reader input)方法,Lucene自己会调用此方法传入文本 看代码 import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; import org.apache.lucene.analysis.tokenattributes.TypeAttribute; import java.io.IOException; // 自定义的DemoTokenizer需要继承org.apache.lucene.analysis.Tokenizer类 // 并实现incrementToken和reset方法,这个方法相当于一个iterator方法,Lucene框架会不停的调用这个方法 // 直到这个方法返回false。调用这个对象的大致的伪代码逻辑如下: // obj = new DemoTokenizer(input); // notEnd? = obj.incrementToken(); // if notEnd? { // read attributes from obj // } // attributes表示在这人类里面调用addAttribute方法添加的attribute,包括分出来的词,类型,位置等等 public class DemoTokenizer extends Tokenizer { // 保存当前读取的字符位置信息 private int position = 0; // 保存分出来的词 protected CharTermAttribute charAttr = addAttribute(CharTermAttribute.class); // 保存当前分出来的词的类型,可以随便定义 protected TypeAttribute typeAttr = addAttribute(TypeAttribute.class); // 保存当前词的位置 private final PositionIncrementAttribute positionAttr = addAttribute(PositionIncrementAttribute.class); // 保存当前词的偏移量 private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); @Override public boolean incrementToken() throws IOException { // 调用这个方法很重要,必须在下面设置attributes之前调用 clearAttributes(); // 保存当前读取到的字符 char[] c = new char[1]; // 读取一个字符到变量c里面 int count = this.input.read(c, this.position, 1); // 返回false代表读完了 if (count == -1) { return false; } // 把当前读取到的字符保存到定义的attributes charAttr.append(c[0]); typeAttr.setType("Char"); positionAttr.setPositionIncrement(position+1); offsetAtt.setOffset(correctOffset(position), correctOffset(position+1)); // 返回true表示后面还有词需要处理,当前还没有读完 return true; } // 正确覆写此方法也非常重要,因为在Lucene框架外面很有可能这个类只会被初始化一次 // 所以如果使用的一些位置信息不正确会导致读取对应的位置错误 @Override public void reset() throws IOException { super.reset(); // 需要把上一个字段使用过的位置信息归0,否则当前这个字段的位置是不正确的 this.position = 0; } } 首先在类的内容需要几个存放Attribute的属性,比如CharTermAttribute charAttr,在调用incrementToken的时候需要先清空再append进charAttr。 ...

October 1, 2017

Lucene7实践入坑第一步

这是一篇很简单的使用Lucene7.0.0来建立索引和搜索的文章。 import org.apache.lucene.analysis.core.WhitespaceAnalyzer; import org.apache.lucene.document.*; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.SearcherManager; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.FSDirectory; import java.io.IOException; import java.nio.file.Paths; public class UseLucene2 { public static void main(String[] args) throws IOException { FSDirectory indexDir = FSDirectory.open(Paths.get("index")); IndexWriterConfig iwc = new IndexWriterConfig(new WhitespaceAnalyzer()); iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE); IndexWriter indexWriter = new IndexWriter(indexDir, iwc); DirectoryReader dir = DirectoryReader.open(indexWriter); Document doc = new Document(); doc.add(new StringField("first", "测试", Field.Store.YES)); indexWriter.addDocument(doc); SearcherManager sm = new SearcherManager(indexWriter, true, true, null); PhraseQuery.Builder builder = new PhraseQuery.Builder(); builder.add(new Term("first", "测试")); PhraseQuery pq = builder.build(); IndexSearcher searcher = sm.acquire(); TopDocs docs = searcher.search(pq, 1); System.out.println(docs.totalHits); for (int i = 0; i < docs.totalHits; i++) { System.out.println(docs.scoreDocs[i]); } sm.close(); indexWriter.close(); } } // TODO 补充解释

August 27, 2017