solr简明教程
文章目录
安装
启动
创建core
配置core索引MySQL数据
3.2.1
3.2.2
3.2.3
测试定时更新
五、配置中文分词
SolrJ 操作索引的增、删、查
七、通过SolrJ对MySQL数据库进行全量更新、增量更新
八、索引高亮显示
九、SolrJ读取富文本创建索引
安装
从官网下载Solr7.4(或本资源包内直接解压,本包内也是官网下载的)
下载地址:https://mirrors.tuna.tsinghua.edu.cn/apache/lucene/solr/7.4.0/
因为我是Windows系统所以选的是.zip的版本,如果是Linux系统需要用.tgz的版本,下面都是以Windows系统下的示例。
Solr7.4是免安装的,解压后就可以直接使用了。
启动
打开之后是这样的
创建core
在cmd窗口输入:
solr create -c testcore
1
testcore 是自己起的名字,回车后会出现下面的信息(还是会有log4j2的错误信息,不用管)
一刷新
Core也可以直接在控制台创建,但是需要自己先再Solr安装根目录/server/solr目录下创建一个core文件夹,而且里面要自己创建一个data文件夹。然后通过控制台的Core Admin创建core 。
个人建议直接在cmd窗口用命令创建,省力省心还可靠。
网上还有自己拷贝conf文件夹到自己创建core 等等的操作,感觉太麻烦。有兴趣的自行百度。
配置core索引MySQL数据
1、把数据库驱动jar包放到
Solr安装根目录/server/solr-webapp/webapp/WEB-INF/lib下
2、把Solr安装根目录/dist文件夹下的
solr-dataimporthandler-x.x.x.jar
solr-dataimporthandler-extras-x.x.x.jar
放到Solr安装根目录/server/solr-webapp/webapp/WEB-INF/lib下
4、Solr安装根目录/server/solr/核心/conf路径下添加文件data-config.xml,并添加以下内容(示范如下)
</entity>
</document>
3.2.1
solr默认使用UTC时间,即与中国时差8小时,所以需要修改配置文件:
Solr安装根目录/bin/solr.in.sh
SOLR_TIMEZONE=”UTC+8”
默认是注释掉的,找到SOLR_TIMEZONE修改后面的值为UTC+8
注意:如果单纯修改solr.in.sh文件没有作用,就修改和solr.in.sh同目录下的solr.cmd文件,修改其中的set SOLR_TIMEZONE=UTC为set SOLR_TIMEZONE=UTC+8
3.2.2
修改mysql数据库的表结构,添加一个时间戳字段,当某行数据发生更新时该字段自动更新为修改数据的时间,为solr增量添加提供服务(范例如下)
ALTER TABLE testdb ADD last_modified TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT current_timestamp
3.2.3
修改solr/server/solr/核心/conf路径下添加文件data-config.xml,并添加增量SQL(示范如下)
就是多添加了deltaQuery部分
</entity>
</document>
4、修改solrconfig.xml,添加以下内容
data-config.xml
5、修改managed-schema,添加mysql中需要存入solr的字段(示范如下)
准备测试效果:
数据库里的数据:
注意:当last_modified的值是0000-00-00 00:00:00时会有错误,
因为java的时间是从1970年开始的,所以不能翻译这个时间,导致的结果就是查不到数据。
现在测试全量更新:依次选择1234
结果是:
测试增量更新,在数据库新添一条数据
注意:增量更新就是只更新有变化的数据,不会更新没有变化的数据。如果增量更新选择clean会删除所有的原索引数据,然后只更新有修改过的数据。
正常使用情况下增量更新的clean是不选择的。这里为了测试更新方式是增量更新的方式,所以选择了clean。切忌用在项目中!!!除非你知道会有什么后果
结果是:
我们看到,之前的两条数据被删除了,只有一条新添加的数据。证明增量更新的功能有效。
四、接下来我们配置一个实用的功能:定时更新
因为数据库的数据可能是动态变化的,为了能和数据库的数据“及时”同步,所以就有了这个功能。
它可以按设定时间自动更新一次索引
4.1修改testcore目录下的配置文件core.properties
name=testcore
config=solrconfig.xml
schema=data-config.xml
dataDir=data
4.2添加实时更新索引相关的jar包:dataimportscheduler-1.2.jar 到
Solr安装根目录/server/solr-webapp/webapp/WEB-INF/lib下
还有两个jar包因为之前已经添加过了就不需要添加了,是
solr-dataimporthandler-x.x.x.jar
solr-dataimporthandler-extras-x.x.x.jar
4.3 修改Solr安装根目录/server/solr-webapp/webapp/WEB-INF/web.xml
把这个添加到里面
org.apache.solr.handler.dataimport.scheduler.ApplicationListener
注意:中间的内容两边不要有空格,容易出现不正常现象
4.4添加更新配置文件:Solr安装根目录/server/solr下新建conf文件夹
在文件夹下新建dataimport.properties文件
#################################################
dataimport scheduler properties
#################################################
to sync or not to sync
1 - active; anything else - inactive
syncEnabled=1
which cores to schedule
in a multi-core environment you can decide which cores you want syncronized
leave empty or comment it out if using single-core deployment
syncCores=game,resource
syncCores=testcore
solr server name or IP address
[defaults to localhost if empty]
server=localhost
solr server port
[defaults to 80 if empty]
port=8983
interval=2
application name/context
[defaults to current ServletContextListener's context (app) name]
webapp=solr
URL params [mandatory]
remainder of URL
params=/dataimport?command=delta-import&clean=false&commit=true
schedule interval
number of minutes between two runs
[defaults to 30 if empty]
重做索引的时间间隔,单位分钟,默认7200,即5天;
为空,为0,或者注释掉:表示永不重做索引
reBuildIndexInterval=7200
重做索引的参数
reBuildIndexParams=/dataimport?command=full-import&clean=true&commit=true
重做索引时间间隔的计时开始时间,第一次真正执行的时间=reBuildIndexBeginTime+reBuildIndexInterval601000;
两种格式:2012-04-11 03:10:00 或者 03:10:00,后一种会自动补全日期部分为服务启动时的日期
reBuildIndexBeginTime=03:10:00
测试定时更新
我们先全量更新一下所有数据,然后在数据库添加一条数据,再等一分钟直接查询
更新前
更新后
五、配置中文分词
solr自带的几个分词器对中文支持并不好,所以需要使用第三方分词器对中文进行分词索引。
常用的分词器有:ansj和ik,前者的效果更好。
注:目前发现ansj分词器索引内容大小超过65248字节时,会报异常,目前尚未找到解决办法
5.1需要的jar包:
ansj_lucene5_plug-5.1.1.2.jar
ansj_seg-5.1.1.jar
ik-analyzer-solr5-5.x.jar
nlp-lang-1.7.2.jar
放到Solr安装根目录/server/solr-webapp/webapp/WEB-INF/lib 中
5.2配置放到Solr安装根目录/server/solr/testcore/conf下的managed-schema
(还是之前配置的那个)
!
然后为要索引的字段添加分词器,例如
1
测试
成功了!
SolrJ 操作索引的增、删、查
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import java.io.IOException;
import java.util.Iterator;
public class IndexOperation {
private static final String urlString = "http://localhost:8983/solr/testcore";
private static SolrClient solr = new HttpSolrClient.Builder(urlString).build();
public void addIndexTest(){//添加一个索引
SolrInputDocument doc = new SolrInputDocument();
//默认id为主键,当id存在时更新数据,否则添加数据
doc.addField("id", "3");
doc.addField("name", "hello world test");
doc.addField("age", "230");
doc.addField("addr", "1111");
try {
solr.add(doc);
solr.commit();
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void deleteIndexTest(String did){
//通过id删除索引
try {
solr.deleteById(did);
solr.commit();
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//通过搜索条件删除索引
//solr.deleteByQuery(query);
}
public void selectIndexTest(){// 查询
SolrQuery query = new SolrQuery();
// *标示多个任意字符,?标示单个任意字符,~模糊搜索
query.setQuery("*:*"); //全搜索
// 分页
query.setStart(0);
query.setRows(10);
QueryResponse queryResponse = null;
try {
queryResponse = solr.query(query);
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
SolrDocumentList docs = queryResponse.getResults();
Iterator<SolrDocument> iter = docs.iterator();
while(iter.hasNext()){
SolrDocument doc = iter.next();
System.out.println(doc.toString());
}
try {
solr.commit();
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//测试
public static void main(String[] args){
IndexOperation ion=new IndexOperation();
ion.addIndexTest();
ion.selectIndexTest();
System.out.println("---------------分割线-----------------");
ion.deleteIndexTest("3");
ion.selectIndexTest();
}
}
七、通过SolrJ对MySQL数据库进行全量更新、增量更新
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
public class LinkMysql {//对数据库进行的操作,全量更新,增量更新
private CloseableHttpClient httpclient = null;
private CookieStore cookieStore = null;
private final String baseSolrUrl;
/**
* 全量导入
*/
private static final String FULL_IMPORT = "full-import";
/**
* 增量导入
*/
private static final String DELTA_IMPORT = "delta-import";
public LinkMysql(String baseSolrUrl) {
if(!baseSolrUrl.endsWith("/")){
baseSolrUrl += "/";
}
this.baseSolrUrl = baseSolrUrl;
}
private synchronized void init(){
if(httpclient == null){
cookieStore = new BasicCookieStore();
httpclient = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).build();
}
}
public void fullImport(String coreName) throws IOException {
init();
HttpPost post = getDataImportPost(coreName, FULL_IMPORT);
httpclient.execute(post);
}
public void deltaImport(String coreName) throws IOException {
init();
HttpPost post = getDataImportPost(coreName, DELTA_IMPORT);
httpclient.execute(post);
}
private HttpPost getDataImportPost(String coreName,String command) throws UnsupportedEncodingException{
HttpPost post = new HttpPost(baseSolrUrl + coreName + "/dataimport?indent=on&wt=json&_=" + new Date().getTime());
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("command", command));
params.add(new BasicNameValuePair("verbose", "false"));
if(command.equals(FULL_IMPORT)){
params.add(new BasicNameValuePair("clean", "true"));
}
params.add(new BasicNameValuePair("commit", "true"));
params.add(new BasicNameValuePair("optimize", "false"));
params.add(new BasicNameValuePair("core", coreName));
params.add(new BasicNameValuePair("name", "dataimport"));
post.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
return post;
}
public void close() throws IOException{
httpclient.close();
httpclient = null;
cookieStore = null;
}
public static void main(String[] args){
//测试
LinkMysql dataClient=new LinkMysql("http://localhost:8983/solr/");
/*全量更新*/
try {
dataClient.fullImport("testcore");
dataClient.close();
} catch (IOException e) {
e.printStackTrace();
}
/*增量更新*/
/* try {
dataClient.deltaImport("testcore");
dataClient.close();
} catch (IOException e) {
e.printStackTrace();
}*/
}
}
八、索引高亮显示
需要有一个实体类
public class TXDocument {
private String id;
private String path;
private String content;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String toString() {
return "TXDocument{" +
"id='" + id + '\'' +
", path='" + path + '\'' +
", content='" + content + '\'' +
'}';
}
}
下面来高亮的代码
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class GaoLiang {
public void setHighL(){
HttpSolrClient solrClient = new HttpSolrClient.Builder("http://127.0.0.1:8983/solr/testcore").build();
SolrQuery query = new SolrQuery();
query.set("q","TName:*回归*");//查询的内容
//1.过滤器
//query.set("fq","pprice:[1 TO 100]");//query.setFilterQueries("pprice:[1 TO 100]");也可以用addFiterQuries设置多过滤条件
//2.排序
//query.set("sort","pprice desc,id asc");//query.setSort("pprice",ORDER.desc); addSort
//3.设置查询到的文档返回的域对象
//query.set("fl","TBPackageName,PackageTypeID");//query.setFields("id,pname");
//4.设置默认查询的域
//query.set("df","pname");
//5.分页
//query.set("start",0); //query.setStart(0)
//query.set("rows",5); //query.setRows(5)
//6.高亮
//query.set("hl",true);
//设置高亮域(设置的域必须在查询条件中存在)
// query.set("h1.fl","TBPackageName");
//前缀
// query.set("hl.simple.pre","<em style='color:red'>");
//后缀
//query.set("hl.simple.post","</em>");
query.setHighlight(true);
query.addHighlightField("TName");
query.setHighlightSimplePre("<em style='color:red'>");
query.setHighlightSimplePost("</em>");
QueryResponse response=null;
{
try {
response = solrClient.query(query);
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
SolrDocumentList results = response.getResults();
System.out.println(results);
//k是id,内部的map的key是域名,其value是高亮的值集合
Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
System.out.println("匹配的结果总数是-------"+results.getNumFound());
for(SolrDocument document:results) {
System.out.println("id----" + document.get("id"));
System.out.println("pname-----" + document.get("TName"));
//System.out.println("pprice------" + document.get("pprice"));
List<String> list = null;
if(highlighting.get(document.get("id")) != null) {
list = highlighting.get(document.get("id")).get("TName");
}else {
System.out.println("无法获取高亮map");
}
System.out.println(list);
if (list != null && list.size() > 0) {
System.out.println("高亮显示的内容:----"+list.get(0));
}else {
System.out.println("高亮显示的内容为空!!!");
}
System.out.println("=========================");
}
}
public static void main(String[] args){
GaoLiang gl=new GaoLiang();
gl.setHighL();
}
}
如果之前你对高亮怎么用在前台显示的文字里,看到这个结果你应该能够猜到了,就是给需要高亮的字段加上了html标签,至于加什么效果,用什么标签就自己决定吧。高亮的意思不是闪闪发亮,只是一个让查询字段特别显示的代名词。
把这个以字符串的形式传给前台……后面就不需要我啰嗦了吧。
九、SolrJ读取富文本创建索引
就是读取word、pdf、xml……等文件的内容创建索引
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FuWenBen {
public static void main(String[] args)
{ //测试
beStart("solr-word.pdf");
}
public static void beStart(String fname){
//富文本存放路径,这是示例就写的简单粗暴一些
String fileUrl = "E:/WorkSpace/ImportData/"+fname;
try
{
indexFilesSolrCell(fname, fname,fileUrl);
}
catch (IOException e)
{
e.printStackTrace();
}
catch (SolrServerException e)
{
e.printStackTrace();
}
}
/**
* @Author:sks
* @Description:获取系统当天日期yyyy-mm-dd
* @Date:
*/
private static String GetCurrentDate(){
Date dt = new Date();
//最后的aa表示“上午”或“下午” HH表示24小时制 如果换成hh表示12小时制
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss aa");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String day =sdf.format(dt);
return day;
}
public static void indexFilesSolrCell(String fileName, String solrId, String path)
throws IOException, SolrServerException
{
String urlString = "http://localhost:8983/solr/testcore";
SolrClient solr = new HttpSolrClient.Builder(urlString).build();
ContentStreamUpdateRequest up = new ContentStreamUpdateRequest("/update/extract");
String contentType = getFileContentType(fileName);
up.addFile(new File(path), contentType);
String fileType = fileName.substring(fileName.lastIndexOf(".")+1);
up.setParam("literal.id", fileName);
up.setParam("literal.path", path);//文件路径
up.setParam("literal.pathuploaddate", GetCurrentDate());//文件上传时间
up.setParam("literal.pathftype", fileType);//文件类型,doc,pdf
up.setParam("fmap.content", "attr_content");//文件内容
up.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
solr.request(up);
}
/**
* @Author:sks
* @Description:根据文件名获取文件的ContentType类型
* @Date:
*/
public static String getFileContentType(String filename) {
String contentType = "";
String prefix = filename.substring(filename.lastIndexOf(".") + 1);
if (prefix.equals("xlsx")) {
contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
} else if (prefix.equals("pdf")) {
contentType = "application/pdf";
} else if (prefix.equals("doc")) {
contentType = "application/msword";
} else if (prefix.equals("txt")) {
contentType = "text/plain";
} else if (prefix.equals("xls")) {
contentType = "application/vnd.ms-excel";
} else if (prefix.equals("docx")) {
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
} else if (prefix.equals("ppt")) {
contentType = "application/vnd.ms-powerpoint";
} else if (prefix.equals("pptx")) {
contentType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
}
else {
contentType = "othertype";
}
return contentType;
}
}
solr简明教程的更多相关文章
- 2013 duilib入门简明教程 -- 第一个程序 Hello World(3)
小伙伴们有点迫不及待了么,来看一看Hello World吧: 新建一个空的win32项目,新建一个main.cpp文件,将以下代码复制进去: #include <windows.h> #i ...
- 2013 duilib入门简明教程 -- 部分bug (11)
一.WindowImplBase的bug 在第8个教程[2013 duilib入门简明教程 -- 完整的自绘标题栏(8)]中,可以发现窗口最大化之后有两个问题, 1.最大化按钮的样式 ...
- 2013 duilib入门简明教程 -- 部分bug 2 (14)
上一个教程中提到了ActiveX的Bug,即如果主窗口直接用变量生成,则关闭窗口时会产生崩溃 如果用new的方式生成,则不会崩溃,所以给出一个临时的快速解决方案,即主窗口 ...
- 2013 duilib入门简明教程 -- 自绘控件 (15)
在[2013 duilib入门简明教程 -- 复杂控件介绍 (13)]中虽然介绍了界面设计器上的所有控件,但是还有一些控件并没有被放到界面设计器上,还有一些常用控件duilib并没有提供(比如 ...
- 2013 duilib入门简明教程 -- 事件处理和消息响应 (17)
界面的显示方面就都讲完啦,下面来介绍下控件的响应. 前面的教程只讲了按钮和Tab的响应,即在Notify函数里处理.其实duilib还提供了另外一种响应的方法,即消息映射DUI_BEG ...
- 2013 duilib入门简明教程 -- FAQ (19)
虽然前面的教程几乎把所有的知识点都罗列了,但是有很多问题经常在群里出现,所以这里再次整理一下. 需要注意的是,在下面的问题中,除了加上XML属性外,主窗口必须继承自WindowImpl ...
- Mac安装Windows 10的简明教程
每次在Mac上安装Windows都是一件非常痛苦的事情,曾经为了装Win8把整台Mac的硬盘数据都弄丢了,最后通过龟速系统恢复模式恢复了MacOSX(50M电信光纤下载了3天才把系统下载完),相信和我 ...
- Docker简明教程
Docker简明教程 [编者的话]使用Docker来写代码更高效并能有效提升自己的技能.Docker能打包你的开发环境,消除包的依赖冲突,并通过集装箱式的应用来减少开发时间和学习时间. Docker作 ...
- 2013 duilib入门简明教程 -- 总结 (20)
duilib的入门系列就到尾声了,再次提醒下,Alberl用的duilib版本是SVN上第个版本,时间是2013.08.15~ 这里给出Alberl最后汇总的一个工程,戳我下载,效 ...
随机推荐
- Redis 持久化(Persistence)
作为内存数据库,Redis 依然提供了持久化机制,其主要目的有两个: 安全:保证进程崩溃后数据不会丢失 备份:方便数据迁移与快速恢复 Redis 同时提供两种持久化机制: RDB 快照:数据库在某个时 ...
- 缓冲区溢出实验 5 Snprintf
实验环境.代码.及准备 https://www.cnblogs.com/lqerio/p/12870834.html vul5 Snprintf函数,百度百科: 将可变个参数(...)按照format ...
- C++11特性-右值引用
什么是左值,什么是右值 常见的误区有 = 左边的是左值,右边的是右值. 左值:具有存储性质的对象,即lvalue对象,是指要实际占用内存空间.有内存地址的那些实体对象,例如:变量(variables) ...
- R语言学习2:绘图
本系列是一个新的系列,在此系列中,我将和大家共同学习R语言.由于我对R语言的了解也甚少,所以本系列更多以一个学习者的视角来完成. 参考教材:<R语言实战>第二版(Robert I.Kaba ...
- Linux command find All In One
Linux command find All In One $ find -h # find: illegal option -- h # usage: # find [-H | -L | -P] [ ...
- ES6 Set vs ES5 Array
ES6 Set vs ES5 Array Set https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Ob ...
- HTML5 & canvas fingerprinting
HTML5 & canvas fingerprinting demo https://codepen.io/xgqfrms/full/BaoMWMp window.addEventListen ...
- CNN专访灵石CTO:Baccarat流动性挖矿能否持续?
近日,CNN记者Robert独家专访Baccarat的项目团队CTO STEPHEN LITAN,跟他特别聊了聊DeFi的近况. 以下是专访全文: Robert:推出Baccarat的契机是什么? S ...
- 人物传记-BILL RAY:低谷时的信念,决定你能走多远
自2018年全球经济危机以来,以工作为重的成年人们一直备受打击.尤其是2020年,全球贸易争端使得经济下滑严重,很多公司倒闭破产,有些人甚至从富豪变成了负债者,走向了人生低谷.其实,每个人都会遇到人生 ...
- CSS前端性能优化
1.Google 资深web开发工程师Steve Souders对CSS选择器的效率从高到低做了一个排序: 1. id选择器(#myid) 2. 类选择器(.myclassname) 3. 标签选择器 ...