solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的文件,生成索引;也可以通过http访问提出查找请求,并得到各种格式的返回结果。
solr本质是基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展。并对查询性能进行了优化,并且提供了一个完善的功能管理界面。
链接:https://pan.baidu.com/s/1q8Ry0peIF3lwOemqxusWMw
提取码:jm3i
(1)启动方式一
启动步骤:
注意: 由于jar包已经内置了一个web服务器(jetty) , 其默认的访问的为8983
(2)启动方式二(推荐)
注:最好重新解压一份Tomcat
启动步骤:
链接:https://pan.baidu.com/s/1mjwOHK1EBizcC5tZcSwCUw
提取码:kugn
为了防止和其他Tomcat端口号冲突,最好在server.xml中更改端口号(都加一)
解决警告信息:
第一步: 将solr的安装包中依赖包的两个目录, 复制到solr的索引库中
第二步: 打开索引库中的collection1中conf目录, 将solrconfif.xml右键打开, 去掉两个../即可
重新运行Tomcat,就不会出现上面的警告信息
solr中支持配置和管理多个索引库, 就像数据库中有多个database是一样的
如何配置多个索引库呢?
简单方案: 将collection1复制一个,然后删除其data文件夹并修改core.properties配置文件即可
重新启动Tomcat,就会出现新添加的索引库2
针对core selector的详细讲解:
solrconfig.xml 配置文件主要定义了 solr 的一些处理规则,包括索引数据的存放 位置,更新,删除,查询的一些规则配置。 一般此文件不需要进行修改, 采取默认即可
Solr中会提前对文档中的字段进行定义,并且在schema.xml中对这些字段的属性进行约束,例如:字段数据类型、字段是否索引、是否存储、是否分词等等
(1)field标签
主要是用来指定字段名称的, Lucene中是有用户在程序中指定, solr中需要提前在配置文件中指定
name:字段的名称
type:字段的类型,里面的内容为fieldType标签的名字
indexed:是否对字段进行索引
stored:是否保存该字段的值
multiValued:该字段是否可以存储多个值
required="true":表示添加文档时,在文档中必须要添加的字段
注:_version_ _root_ id 保留不要删掉
(2)dynamicField标签
被称为是动态字段
此种标签是为程序的扩展所使用的,因为我们不可能把所有的字段全部定义好,所以就需要动态与来进行动态扩展,弥补field标签无法详细列举的字段
(3)uniqueKey标签
必要标签, 表名文档的唯一属性, 一般默认为id
Lucene中是自己进行维护, solr中, 需要自己指定
(4)copyField标签
被称为是复制域
source: 表名要复制那个字段的值
dest: 复制到那个字段上
原字段和目标字段必须是已经使用field字段定义,而且,dest字段最好是多值字段,主要目的是用于查询
(5)fieldType标签
此种标签是用来定义字段的类型的,可以指定此字段使用何种分词器进行分词
name:字段类型的名字,用户执行field字段的字段类型
class:solr的字段类型
textFile在配置字段类型时,需要指定分词器
solrj是Apache官方提供的一套java开发的, 用于操作solr服务的API, 通过这套API可以让程序与solr服务进行交互, 让java程序可以直接操作solr服务进行增删改查
准备数据:如果想添加昨天爬取的新闻数据,修改约束文件
将自带的field删掉(除了上面强调的三个不能删);将copyField字段也删除;将下面的添加在文件中
导入相关的jar包
org.apache.solr
solr-solrj
4.10.2
commons-logging
commons-logging-api
1.1
junit
junit
4.12
@Test
public void testWriteIndex() throws Exception{
//创建solr服务对象
SolrServer server = new HttpSolrServer("http://localhost:8081/solr/collection1");
//文档对象
SolrInputDocument document = new SolrInputDocument();
document.addField("id",1);
document.addField("title","于正回应秒删微博:说的不是赵丽颖和陈晓,大家散了吧!");
document.addField("intro","于正回应秒删微博:说的不是赵丽颖和陈晓,大家散了吧!说到于正,相信大家一定都不陌生吧?近两年来,于正这个名字在圈内是非常火的!有非常多的好作品都是由他“制作的”,例如:《美人心计》《宫锁心玉》!就在……");
//添加文档对象
server.add(document);
//写入索引
server.commit();
}
@Test
public void testWriteIndex2() throws Exception{
//创建solr服务对象
SolrServer server = new HttpSolrServer("http://localhost:8081/solr/collection1");
//文档对象
SolrInputDocument document1 = new SolrInputDocument();
document1.addField("id",2);
document1.addField("title","杨颖曝人生第一份工作,15岁当模特挣600港币,吴亦凡则是洗碗");
document1.addField("intro","最近,吴亦凡、Angelababy(杨颖)、赵今麦、福克斯相聚于综艺《潮流合伙人》,在日本东京合开一家潮流集合店。在最新一期的节目中,Angelababy一行四人在结束了一天的辛苦工作后,带着\"丰收……");
SolrInputDocument document2 = new SolrInputDocument();
document2.addField("id",3);
document2.addField("title","《庆余年》之后,阅文男频IP的爆款指南");
document2.addField("intro","《庆余年》爆了!屠榜各大社交平台,连平时很少追剧的小哥哥们,都开始讨论起《庆余年》了。作为引领行业的正版数字阅读平台和文学 IP 培育平台,阅文集团一直在探索男频IP开发的多种可能性,《庆余年》之后……");
List dosc = new ArrayList();
dosc.add(document1);
dosc.add(document2);
//添加文档对象
server.add(dosc);
//写入索引
server.commit();
}
public class News {
@Field
private Integer id;
@Field
private String title;
@Field
private String intro;
@Field
private String source;
@Field
private String vurl;
@Field
private Date publishTime;
@Override
public String toString() {
return "News{" +
"id=" + id +
", title='" + title + '\'' +
", intro='" + intro + '\'' +
", source='" + source + '\'' +
", vurl='" + vurl + '\'' +
", publishTime=" + publishTime +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getVurl() {
return vurl;
}
public void setVurl(String vurl) {
this.vurl = vurl;
}
public Date getPublishTime() {
return publishTime;
}
public void setPublishTime(Date publishTime) {
this.publishTime = publishTime;
}
}
@Test
public void testWriteIndex3() throws Exception{
//创建solr服务对象
SolrServer server = new HttpSolrServer("http://localhost:8081/solr/collection1");
News news = new News();
news.setId(4);
news.setTitle("这是标题");
news.setIntro("这是内容部分");
news.setSource("这是作者");
news.setVurl("https://www.baidu.com");
Long dateLong = new Date().getTime()* 1000 * 3600 * 8;
news.setPublishTime(new Date(dateLong));
server.addBean(news);
server.commit();
}
@Test
public void testUpdateIndex() throws Exception{
//创建solr服务对象
SolrServer server = new HttpSolrServer("http://localhost:8081/solr/collection1");
//文档对象
SolrInputDocument document = new SolrInputDocument();
document.addField("id",1);
document.addField("title","一口气看完三集,BBC的重口味英剧终于开播了");
document.addField("intro","吸血鬼题材类的影视剧,一直都是影视界的宠儿。前前后后,火了很多该类的作品:HBO的《真爱如血》;舔屏级颜值的《暮光之城》;狗血多角恋的《吸血鬼日记》...诸如此类的剧集,好像都在告诉我们吸血鬼并不是……");
server.add(document);
server.commit();
}
@Test
public void testDelIndex() throws Exception{
//创建solr服务对象
SolrServer server = new HttpSolrServer("http://localhost:8081/solr/collection1");
//server.deleteById("1");根据id删除
//server.deleteByQuery("*:*");删除所有
server.deleteByQuery("title:标题");//通过查询字符串进行删除
server.commit();
}
@Test
public void testQuery() throws Exception{
//创建solr服务对象
SolrServer server = new HttpSolrServer("http://localhost:8081/solr/collection1");
//创建solr的查询对象
SolrQuery query = new SolrQuery("*:*");//查询所有
QueryResponse response = server.query(query);
//文档的集合
SolrDocumentList documentList = response.getResults();
for(SolrDocument document:documentList){
String id = document.get("id").toString();
String title = document.get("title").toString();
System.out.println("id:"+id+"title:"+title);
}
}
返回javaBean
@Test
public void testQuery2() throws Exception{
//创建solr服务对象
SolrServer server = new HttpSolrServer("http://localhost:8081/solr/collection1");
//创建Solr的查询对象
SolrQuery query = new SolrQuery("*:*");
QueryResponse response = server.query(query);
List beans = response.getBeans(News.class);
for (News news : beans) {
System.out.println(news);
}
}
注:这里会报错。因为实体类的id属性是Integer类型的,而约束文件中的id是string类型的,解决方法,将实体类的id属性换成String类型。转换成JavaBean这种查询方式不推荐
准备数据:添加多条索引(随意)
// 抽取出一个方法
public void query(SolrQuery query) throws Exception{
SolrServer server = new HttpSolrServer("http://localhost:8081/solr/collection1");
QueryResponse response = server.query(query);
SolrDocumentList documentList = response.getResults();
for(SolrDocument document:documentList){
String id = document.get("id").toString();
//String title = document.get("title").toString();
//System.out.println("id:"+id+"title:"+title);
System.out.println("id:"+id);
}
}
//词条查询
@Test
public void testTerm() throws Exception{
/*
* 字段名:关键字
* 在查询时,会对关键字进行分词
* */
// SolrQuery query = new SolrQuery("*:*");
SolrQuery query = new SolrQuery("title:今天");
query(query);
}
//通配符查询
@Test
public void testWildCard() throws Exception{
/*
* *:匹配0到多个字符
* ?:匹配1个字符
* */
//SolrQuery query = new SolrQuery("title:awkwafina");
//SolrQuery query = new SolrQuery("title:awkwafin?");
SolrQuery query = new SolrQuery("title:awkwafi*");
query(query);
}
//模糊查询
@Test
public void testFuzzy() throws Exception{
/*
* 在关键字之后添加~ 表示模糊查询
* 最大编辑次数:通过新增、修改、删除可以匹配正确词条的次数,默认也为2
* ~后的数字:表示最大编辑次数
* */
//SolrQuery query = new SolrQuery("title:wkwafin~");
SolrQuery query = new SolrQuery("title:wkwafin~1");//最大编辑次数设置为1
query(query);
}
//范围查询
@Test
public void testRange() throws Exception{
/*
* id是string类型的,是按字典顺序进行排序
* 所以id:3也被查找出来
*
* []包含最大值和最小值,{}不包含最大值和最小值
* */
//SolrQuery query = new SolrQuery("id:[20 TO 40]");
//SolrQuery query = new SolrQuery("publishTime:[2010-01-01T12:00:00Z TO 2030-01-01T12:00:00Z]");
SolrQuery query = new SolrQuery("publishTime:{2010-01-01T12:00:00Z TO 2030-01-01T12:00:00Z}");
query(query);
}
//组合查询
@Test
public void testBoolean() throws Exception{
// SolrQuery query = new SolrQuery("publishTime:{2010-01-01T12:00:00Z TO 2030-01-01T12:00:00Z} OR title:awkwafi*");
SolrQuery query = new SolrQuery("NOT publishTime:{2010-01-01T12:00:00Z TO 2030-01-01T12:00:00Z}");
query(query);
}
总结:
1、匹配所有文档:*:* (通配符?和 * :“*”表示匹配任意字符;“?”表示匹配出现的位置)
2、布尔操作:AND、OR和NOT布尔操作(推荐使用大写,区分普通字段)
3、子表达式查询(子查询):可以使用“()”构造子查询。 比如:(query1 AND query2) OR (query3 AND query4)
4、相似度查询:
(1)默认相似度查询:title:appla~,此时默认编辑距离是2
(2)指定编辑距离的相似度查询:对模糊查询可以设置编辑距离,可选02的整数:title:appla1。
5、范围查询(Range Query):Lucene支持对数字、日期甚至文本的范围查询,并且两端范围。结束的范围可以使用“*”通配符。
(1)日期范围(ISO-8601 时间GMT):birthday:[1990-01-01T00:00:00.000Z TO 1999-12-31T24:59:99.999Z]
(2)数字:age:[2000 TO *]
(3)文本:content:[a TO a]
//排序
@Test
public void testSort() throws Exception{
//字典顺序
// 1 10 2 23 3
//注:这里的id是string类型,按字典顺序排
SolrQuery query = new SolrQuery("*:*");
query.setSort("id",SolrQuery.ORDER.desc);
query(query);
}
//分页
@Test
public void testPage() throws Exception{
int currentPage = 2;
int pageSize = 3;
SolrQuery query = new SolrQuery("*:*");
//分页
int start = (currentPage - 1 ) * pageSize;
query.setStart(start);
query.setRows(pageSize);
query(query);
}
将抽取的方法返回值改为QueryResponse
// 抽取出一个方法2
public QueryResponse query2(SolrQuery query) throws Exception{
SolrServer server = new HttpSolrServer("http://localhost:8081/solr/collection1");
QueryResponse response = server.query(query);
SolrDocumentList documentList = response.getResults();
for(SolrDocument document:documentList){
String id = document.get("id").toString();
String title = document.get("title").toString();
System.out.println("id:"+id+"title:"+title);
}
return response;
}
//高亮
@Test
public void testHighlighting() throws Exception{
SolrQuery query = new SolrQuery("title:哒哒哒");
//开启高亮
query.setHighlight(true);
//设置高亮的字段
query.addHighlightField("title");
//query.addHighlightField("content");可以添加多个
query.setHighlightSimplePre("");
query.setHighlightSimplePost("");
QueryResponse response = query2(query);
/*
* 最外层的map的key为查询到的文档的id
* 外层map的value为高亮的内容
*
* 内层map的key为高亮的字段的名字
* 内层map的value为高亮的内容
*
* 内层map的value为list,通常只有一条数据
*/
Map>> outMap = response.getHighlighting();
for(String docId : outMap.keySet()){
Map> innerMap = outMap.get(docId);
for(String fileName : innerMap.keySet()){
List contents = innerMap.get(fileName);
System.out.println(contents.get(0));
}
}