目录
Java中使用Solr,历史版本(7.0.0之后、5.0.0~6.6.6、4.10.4之前)
Java中使用Solr,历史版本(7.0.0之后、5.0.0~6.6.6、4.10.4之前)
导入依赖
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>8.5.1</version>
</dependency>
<!--日志的包, solrj执行需要一个日志包 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
新版本(7.0.0之后)
Solr的所有请求均由SolrClient发送,具有灵活性。SolrClient是SolrJ核心。负责连接到Solr并与之通信的工作,并且是大多数用户配置的发生地。请求以的形式发送,并以形式SolrRequests返回SolrResponses
注意:新版本Solr获取SolrClient的方式也和之前旧版本有所不同。Solr 6.5开始不推荐直接使用HttpSolrClient的构造方法创建SolrClient
从7.0.0开始删除了HttpSolrClient创建客户端的构造方法,使用内部类Builder构建SolrClient
SolrClients类型
SolrClient有一些具体的实现,每个实现都针对不同的使用模式或弹性模型
Client类型 | 描述 |
---|---|
HttpSolrClient | 面向以查询为中心的工作负载,尽管它也是一个很好的通用客户端。直接与单个Solr节点通信 |
Http2SolrClient | 利用HTTP/2的异步,非阻塞和通用客户端。此类是实验性的,因此它的API可能会在次要版本的SolrJ中更改或删除 |
LBHttpSolrClient | 在整个Solr节点列表之间平衡请求负载。根据节点运行状况调整“服务中”节点的列表 |
LBHttp2SolrClient | 就像LBHttpSolrClient但Http2SolrClient改用。此类是实验性的,因此它的API可能会在次要版本的SolrJ中更改或删除 |
CloudSolrClient | 旨在与SolrCloud部署进行通信。使用已经记录的ZooKeeper状态来发现请求并将其路由到健康的Solr节点 |
ConcurrentUpdateSolrClient | 面向以索引为中心的工作负载。在将较大的批次发送到Solr之前,在内部缓冲文档 |
ConcurrentUpdateSolrClient | 就像ConcurrentUpdateSolrClient但Http2SolrClient改用。此类是实验性的,因此它的API可能会在次要版本的SolrJ中更改或删除 |
基本网址
大多数SolrClient实现(CloudSolrClient和Http2SolrClient除外)都要求用户指定一个或多个Solr基本URL,然后客户端将其用于将HTTP请求发送到Solr。从那时起,用户在其提供的基本URL上包括的路径会影响所创建客户端的行为
(1)具有指向特定核心或集合路径的URL
(例如:http://hostname:8983/solr/core1)。如果在基本URL中指定了核心或集合,则不需要对该客户端进行的后续请求来重新指定受影响的集合。但是,客户端仅限于将请求发送到该核心/集合,而不能将请求发送到任何其他请求
SolrClient client = new HttpSolrClient.Builder("http://my-solr-server:8983/solr/core1").build();
QueryResponse response = client.query(new SolrQuery("*:*"));
效果:不可以选择其他内核查询,但是在执行请求时不需要再声明指定内核
(2)指向根Solr路径的URL
(例如:http://hostname:8983/solr)。如果在基本URL中未指定核心或集合,则可以对任何核心/集合进行请求,但是必须在所有请求上指定受影响的核心/集合
SolrClient client = new HttpSolrClient.Builder("http://my-solr-server:8983/solr").build();
QueryResponse response = client.query("core1", new SolrQuery("*:*"));
效果:更加灵活的运用,可以向任意内核发送请求,但是请求必须声明内核名
一般来说,如果SolrClient仅用于单个核心/集合,则在路径中包括该实体是最方便的。如果需要更大的灵活性,则应排除集合/核心
创建HttpSolrClient
//solr4创建方式
SolrServer solrServer = new HttpSolrServer("http://localhost:8983/solr/core");
//solr5创建方式,在url中指定core名称
HttpSolrClient solrServer=new HttpSolrClient("http://localhost:8983/solr/core");
//solr7创建方式,在url中指定core名称
HttpSolrClient solrServer = new HttpSolrClient
.Builder("http://localhost:8983/solr/core").build();
final String solrUrl = "http://localhost:8983/solr/core";
//创建solrClient同时指定超时时间,不指定走默认配置
HttpSolrClient build = new HttpSolrClient.Builder(solrUrl)
.withConnectionTimeout(10000)
.withSocketTimeout(60000)
.build();
<!--整合solr服务器与客户端信息 -->
<context:property-placeholder location="classpath:solr.properties" />
<!-- Spring配置solrJ -->
<bean id="builder" class="org.apache.solr.client.solrj.impl.HttpSolrClient.Builder">
<!-- solrJ内核url -->
<constructor-arg name="baseSolrUrl" value="${solr.url}"/>
</bean>
<bean id="httpSolrClient" class="org.apache.solr.client.solrj.impl.HttpSolrClient">
<constructor-arg name="builder" ref="builder"/>
</bean>
SolrJ的增删改查(单机版API操作)
1.创建索引
(1)指定id单条创建索引
String solrUrl = "http://node21:8080/solr/new_core";
HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
// 创建索引文档对象
SolrInputDocument doc = new SolrInputDocument();
// 第一个参数:域的名称,域的名称必须是在schema.xml中定义的
// 第二个参数:域的值,注意:id的域不能少
doc.addField("id", "1");
doc.addField("name", "红豆");
doc.addField("price", "1.2");
// 3.将文档写入索引库中
solrClient.add(doc);
solrClient.commit();
(2)批量创建索引
String solrUrl = "http://node21:8080/solr/new_core";
HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
// 创建索引文档对象
SolrInputDocument doc1 = new SolrInputDocument();
doc1.addField("id", "2");
doc1.addField("name", "绿豆");
doc1.addField("price", 1.8);
SolrInputDocument doc2 = new SolrInputDocument();
doc2.addField("id", "3");
doc2.addField("name", "黑豆");
doc2.addField("price", 2.6);
List<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
docs.add(doc1);
docs.add(doc2);
// 3.将文档写入索引库中
solrClient.add(docs);
solrClient.commit();
2.更新索引
String solrUrl = "http://node21:8080/solr/new_core";
HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
// 创建索引文档对象
SolrInputDocument doc = new SolrInputDocument();
// 把红豆价格修改为1.5
doc.addField("id", "1");
doc.addField("name", "红豆");
doc.addField("price", "1.5");
// 3.将文档写入索引库中
solrClient.add(doc);
solrClient.commit();
// 提交
solrClient.commit();
3.删除索引
(1)单一条件删除
String solrUrl = "http://node21:8080/solr/new_core";
HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
// 全删
// solrClient.deleteByQuery("*:*");
// 模糊匹配删除(带有分词效果的删除)
solrClient.deleteByQuery("name:红");
// 指定id删除
// solrClient.deleteById("1");
solrClient.commit();
(2)批量条件删除
String solrUrl = "http://node21:8080/solr/new_core";
HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
// 通过id删除
ArrayList<String> ids = new ArrayList<String>();
ids.add("2");
ids.add("3");
solrClient.deleteById(ids);
// [3]提交
solrClient.commit();
// [4]关闭资源
solrClient.close();
4.查询索引(重点)
SolrClient有很多query()方法可以从Solr获取结果。这些方法中的每一个都采用SolrParams一个对象,该对象封装了任意查询参数。每个方法都输出一个QueryResponse包装器,该包装器可用于访问结果文档和其他相关的元数据
SolrParams有一个SolrQuery子类,它提供了一些方便的方法,可以大大简化查询的创建
1.基本查询
(1)匹配查询
String solrUrl = "http://localhost:8983/solr/mycore";
HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
// 创建搜索对象
SolrQuery query = new SolrQuery();
// 设置搜索条件
query.set("q", "*:*");
// 设置每页显示多少条
query.setRows(2);
// 发起搜索请求
QueryResponse response = solrClient.query(query);
// 查询结果
SolrDocumentList docs = response.getResults();
// 查询结果总数
long cnt = docs.getNumFound();
System.out.println("总条数为" + cnt + "条");
for (SolrDocument doc : docs) {
System.out.println("id:" + doc.get("id") + ",name:" + doc.get("name"));
}
solrClient.close();
(2)条件过滤查询
String solrUrl = "http://node21:8080/solr/new_core";
HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl).build();
// 2 封装查询参数
Map<String, String> queryParamMap = new HashMap<String, String>();
queryParamMap.put("q", "*:*");
// 3 添加到SolrParams对象,SolrParams 有一个 SolrQuery 子类,提供了一些方法极大地简化了查询操作
MapSolrParams queryParams = new MapSolrParams(queryParamMap);
// 4 执行查询返回QueryResponse
QueryResponse response = solrClient.query(queryParams);
// 5 获取doc文档
SolrDocumentList docs = response.getResults();
// 查询结果总数
long cnt = docs.getNumFound();
System.out.println("总条数为" + cnt + "条");
// [6]内容遍历
for (SolrDocument doc : docs) {
System.out.println("id:" + doc.get("id") + ",name:" + doc.get("name"));
}
solrClient.close();
2.复杂查询
服务对象
public void query(SolrQuery query) throws Exception {
// 获取连接
String solrUrl = "http://127.0.0.1:8983/solr/core1";
// 创建solrClient同时指定超时时间,不指定走默认配置
HttpSolrClient client = new HttpSolrClient.Builder(solrUrl)
.withConnectionTimeout(10000)
.withSocketTimeout(60000)
.build();
// 创建Solr的查询对象
QueryResponse response = client.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);
}
}
(1)词条查询
SolrQuery query = new SolrQuery("title:钱");
query(query);
(2)通配符查询
匹配0到多个字符 ?:匹配1个字符
SolrQuery query = new SolrQuery("title:selin*");
query(query);
(3)模糊查询
/**
* 在关键字之后添加~,表示进行模糊查询。最大编辑次数:通过新增、修改、删除可以匹配正确的词条的次数,默认为2~后的数据,表示最大的编辑次数
*/
SolrQuery query = new SolrQuery("title:selinaq~1");
query(query);
(4)范围查找查询
// SolrQuery query = new SolrQuery("id:[20 TO 50]");
SolrQuery query = new SolrQuery("publishTime:{2001-01-01T12:00:00Z TO 2019-01-01T12:00:00Z}");
query(query);
(5)组合
//AND OR NOT
SolrQuery query = new SolrQuery(" title:xxx or inrot:xxx");
query(query);
.
3.高级使用
(1)solr的排序
SolrQuery solrQuery = new SolrQuery("*:*");
solrQuery.setSort("id", SolrQuery.ORDER.asc);
query(solrQuery);
(2)solr的分页
// 当前页
int page = 2;
// 每页条数
int pageSize = 3;
SolrQuery solrQuery = new SolrQuery("*:*");
// 排序
solrQuery.setSort("id", SolrQuery.ORDER.asc);
// 分页
solrQuery.setStart((page - 1) * pageSize);
solrQuery.setRows(pageSize);
query(solrQuery);
(3)solr 的高亮
int page = 1; //当前页
int pageSize = 3; //每页条数
SolrQuery solrQuery = new SolrQuery("content:lucene");
// 排序
solrQuery.setSort("id", SolrQuery.ORDER.asc);
// 分页
solrQuery.setStart((page - 1) * pageSize);
solrQuery.setRows(pageSize);
solrQuery.setHighlight(true); // 开启了高亮
solrQuery.addHighlightField("content");
//使用add方法来设置高亮字段,说明高亮字段可以有多个
solrQuery.addHighlightField("title");
solrQuery.setHighlightSimplePre("<font color='red'>");
solrQuery.setHighlightSimplePost("</font>");
// 2. 执行查询
QueryResponse response = client.query(solrQuery);
/*
* 最外层的map: key: 文档的id value: 文档的高亮内容 内层的map: key: 高亮的字段 value: 这个字段的高亮内容
* list集合: 高亮内容, 而且集合中一般只有一个数据, 除非高亮的字段是一个多值的字段,并且设置高亮的最大分片数大于1
*/
Map<String, Map<String, List<String>>> map = response.getHighlighting();
for (String docId : map.keySet()) {
Map<String, List<String>> listMap = map.get(docId);
for (String filed : listMap.keySet()) {
List<String> list = listMap.get(filed);
System.out.println(list.get(0) + " " + list.size());
}
}
Java对象绑定
尽管SolrJ提供的UpdateResponse和QueryResponse接口非常有用,但是使用特定于域的对象通常更方便,这些对象可以被您的应用程序更容易地理解。值得庆幸的是,SolrJ通过隐式地将文档与任何已特别标记为@Field注释的类之间来回转换来支持此操作
可以使用@Field注释将Java对象中的每个实例变量映射到相应的Solr字段。Solr字段默认情况下共享带注释的变量的名称,但是,可以通过为注释提供显式字段名称来覆盖此名称。
使用@FieId注解字段
public class Product {
@Field
private String id;
@Field
private String p_name;
//省略getter/setter方法、构造方法
}
1.通过对象创建索引
// [1]获取连接
String solrUrl = "http://127.0.0.1:8983/solr/core1";
// [2]创建solrClient同时指定超时时间,不指定走默认配置
HttpSolrClient client = new HttpSolrClient.Builder(solrUrl)
.withConnectionTimeout(10000)
.withSocketTimeout(60000)
.build();
// [3]创建对象
Product product = new Product();
product.setId("30000");
product.setP_name("测试商品名称");
// [4]添加对象
UpdateResponse response = client.addBean(product);
// [5]提交操作
client.commit();
// [6]关闭资源
client.close();
2.通过deleteByQuery删除索引
String solrUrl = "http://127.0.0.1:8080/solr/core1";
//创建solrClient同时指定超时时间,不指定走默认配置
HttpSolrClient client = new HttpSolrClient.Builder(solrUrl)
.withConnectionTimeout(10000)
.withSocketTimeout(60000)
.build();
//[2]执行删除
client.deleteByQuery("id:100");
//[3]提交操作
client.commit();
//[4]关闭资源
client.close();
3.通过对象索引查询
String solrUrl = "http://127.0.0.1:8080/solr/core1";
// 创建solrClient同时指定超时时间,不指定走默认配置
HttpSolrClient client = new HttpSolrClient.Builder(solrUrl)
.withConnectionTimeout(10000)
.withSocketTimeout(60000)
.build();
// [2]创建SolrQuery对象
SolrQuery query = new SolrQuery("*:*");
// 添加回显的内容
query.addField("id");
query.addField("p_name");
query.setRows(200);// 设置每页显示多少条
// [3]执行查询返回QueryResponse
QueryResponse response = client.query(query);
// [4]获取doc文档
List<Product> products = response.getBeans(Product.class);
// [5]遍历
for (Product product : products) {
System.out.println("id:" + product.getId()+"p_name:" + product.getP_name());
}
client.close(); // 关闭资源
老版本(5.0.0~6.6.6)
5版本还能使用过时的HttpSolrServer。6版本就不复存在了
在solr 5系之后跟solr4最大的区别是被发布成了一个独立的应用。而不再需要tomcat等容器。在其内部集成了jetty服务器,他可以通过bin目录的脚本直接运行启动。solr5有两种运行模式,独立模式和云模式,独立模式是以core来管理,云模式是以collection来管理
使用的是实例化HttpSolrClient来操作Solr索引库
new HttpSolrClient("http://127.0.0.1:8983/solr/core1");
老旧版本(4.10.4之前)
4.10.4为最后一个版本
(1)新增索引
1.原生方式
// 1. 创建solr的服务对象(发送请求, 获取数据)
SolrServer solrServer = new HttpSolrServer("http://localhost:8983/solr/core");
// 2. 添加索引
/* name的字段名必须存在,配置在schema.xml */
List<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", "2");
doc.addField("title", "双十一来了");
doc.addField("content", "又到了卖肾的季节了");
docs.add(doc);
SolrInputDocument doc1 = new SolrInputDocument();
doc1.addField("id", "3");
doc1.addField("title", "剁手节来了");
doc1.addField("content", "今天你剁手了吗?");
docs.add(doc1);
solrServer.add(docs);
// 3. 提交数据
solrServer.commit();
2.一次写入多条索引
// 1. 创建solrServer对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8983/solr/core");
// 2. 设置多条数据
List<SolrInputDocument> docs = new ArrayList<>();
for (int i = 0; i < 10; i++) {
// 2.1 创建一个document
SolrInputDocument document = new SolrInputDocument();
document.addField("id", i);
document.addField("content", "solr是一个独立的企业级搜索应用服务器, 可以通过http请求访问这个服务器, 获取或者写入对应的内容, 其底层是Lucene " + i);
document.addField("title", "solr的简介");
// 2.2 将document添加到集合中
docs.add(document);
}
// 3. 写入索引
solrServer.add(docs);
// 4. 提交索引
solrServer.commit();
3.使用JavaBean来添加索引数据
// 1. 创建 solr的服务对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8983/solr/core");
// 2. 添加文档数据
News news = new News("3", "Ella","http://ent.163.com/18/1107/10/E00KM09000038FO9.html");
solrServer.addBean(news);
// 使用addBeans来添加多条数据
// 3. 提交数据
solrServer.commit();
(2)删除索引库
// 1. 创建solr的服务对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8983/solr/core");
// 2. 执行删除
// solrServer.deleteByQuery("*:*"); 删除全部的数据
solrServer.deleteById("1"); // 删除id为1的数据
// 3. 提交数据
solrServer.commit();
(3)查询索引库
1.基本查询
(1)查询全部
//1. 创建 solrServer对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8983/solr/core");
//2. 执行查询
//SolrQuery : solr的查询对象
SolrQuery solrQuery = new SolrQuery("*:*");
QueryResponse response = solrServer.query(solrQuery);
//3. 获取数据
SolrDocumentList documentList = response.getResults();
for (SolrDocument document : documentList) {
String id = (String) document.get("id");
String title = (String) document.get("title");
String content = (String) document.get("content");
String url = (String) document.get("url");
System.out.println("id "+id+" title"+title+" content"+content+" url"+url);
}
(2)查询对象
//1. 创建 solrServer对象
SolrServer solrServer = new HttpSolrServer("http://localhost:8983/solr/core");
//2. 执行查询
//SolrQuery : solr的查询对象
SolrQuery solrQuery = new SolrQuery("*:*");
QueryResponse response = solrServer.query(solrQuery);
//3. 获取数据 : 此处有可能会出现转换类型异常的错误:
List<News> newsList = response.getBeans(News.class);
for (News news : newsList) {
System.out.println(news.getId()+" "+news.getTitle());
}
2.复杂查询
服务对象
public void query(SolrQuery query) throws Exception {
// 创建SolrServer服务对象
SolrServer server = new HttpSolrServer("http://localhost:8983/solr/core");
// 创建solr的查询对象
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);
}
}
(1)词条查询
SolrQuery query = new SolrQuery("title:钱");
query(query);
(2)通配符查询
匹配0到多个字符 ?:匹配1个字符
SolrQuery query = new SolrQuery("title:selin*");
query(query);
(3)模糊查询
/*
* 在关键字之后添加~,表示进行模糊查询 最大编辑次数:通过新增,修改,删除可以匹配正确的词条的次数,默认为2 ~后的数据,表示最大的编辑次数
*/
SolrQuery query = new SolrQuery("title:selinaq~1");
query(query);
(4)范围查找查询
// SolrQuery query = new SolrQuery("id:[20 TO 50]");
SolrQuery query = new SolrQuery("publishTime:{2001-01-01T12:00:00Z TO 2019-01-01T12:00:00Z}");
query(query);
(5)组合
//AND OR NOT
SolrQuery query = new SolrQuery("id:[20 TO 50]");
SolrQuery query = new SolrQuery(" title:xxx or inrot:xxx");
query(query);
3.高级使用
(1)solr的排序
SolrQuery solrQuery = new SolrQuery("*:*");
solrQuery.setSort("id", SolrQuery.ORDER.asc);
query(solrQuery);
(2)solr的分页
int page = 2; // 当前页
int pageSize = 3;// 每页条数
SolrQuery solrQuery = new SolrQuery("*:*");
// 排序
solrQuery.setSort("id", SolrQuery.ORDER.asc);
// 分页
solrQuery.setStart((page - 1) * pageSize);
solrQuery.setRows(pageSize);
query(solrQuery);
(3)solr 的高亮
// 创建solr的服务对象
SolrQuery solrServer = new HttpSolrServer("http://localhost:8983/solr/core");
int page = 1; //当前页
int pageSize = 3; //每页条数
SolrQuery solrQuery = new SolrQuery("content:lucene");
// 排序
solrQuery.setSort("id", SolrQuery.ORDER.asc);
// 分页
solrQuery.setStart((page - 1) * pageSize);
solrQuery.setRows(pageSize);
// 高亮:
solrQuery.setHighlight(true); //开启了高亮
solrQuery.addHighlightField("content");
//使用add方法来设置高亮字段, 说明高亮字段可以有多个
solrQuery.addHighlightField("title");
solrQuery.setHighlightSimplePre("<font color='red'>");
solrQuery.setHighlightSimplePost("</font>");
// 2. 执行查询
QueryResponse response = solrServer.query(solrQuery);
/*
* 最外层的map: key: 文档的id value: 文档的高亮内容 内层的map: key: 高亮的字段 value: 这个字段的高亮内容
* list集合: 高亮内容, 而且集合中一般只有一个数据, 除非高亮的字段是一个多值的字段,并且设置高亮的最大分片数大于1
*/
Map<String, Map<String, List<String>>> map = response.getHighlighting();
for (String docId : map.keySet()) {
Map<String, List<String>> listMap = map.get(docId);
for (String filed : listMap.keySet()) {
List<String> list = listMap.get(filed);
System.out.println(list.get(0) + " " + list.size());
}
}