接入 SolrJ 8.0.0 时,旧教程中的 SolrServer 与手上依赖不一致。我从 JAR 和 IDE 类层次找到 SolrClient,再使用 HttpSolrClient.Builder 创建客户端。查当前依赖里的 API,比反复猜旧类名更直接。
创建与关闭客户端
SolrClient 是抽象入口,HttpSolrClient 提供 HTTP 实现。单次演示可以用 try-with-resources;长期服务通常复用客户端,并在组件销毁时关闭,不要每次查询都创建连接池。Solr 8.0 SolrJ 指南
下面代码面向已有的隔离演示 core,不是服务端安装教程。执行前需准备 id 为 uniqueKey、book_name 可写可查且可返回并支持高亮的 schema。不要把演示 URL 指到生产 core。
恢复单文档读写结构
import java.io.IOException;
import java.util.List;
import java.util.Map;
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.SolrInputDocument;
public class Solr8Examples {
private static final String DEMO_ID = "demo-solrj8-001";
private static SolrInputDocument document(String title) {
SolrInputDocument doc = new SolrInputDocument();
doc.setField("id", DEMO_ID);
doc.setField("book_name", title);
return doc;
}
public static void add(SolrClient client)
throws IOException, SolrServerException {
client.add(document("Solr 入门"));
client.commit();
}
public static void replace(SolrClient client)
throws IOException, SolrServerException {
client.add(document("SolrJ 8 入门"));
client.commit();
}
public static void deleteDemo(SolrClient client)
throws IOException, SolrServerException {
client.deleteById(DEMO_ID);
client.commit();
}
public static void query(SolrClient client)
throws IOException, SolrServerException {
SolrQuery query = new SolrQuery("book_name:Solr");
query.addFilterQuery("id:" + DEMO_ID);
query.addSort("id", SolrQuery.ORDER.asc);
query.setStart(0);
query.setRows(5);
query.setFields("id", "book_name");
query.setHighlight(true);
query.addHighlightField("book_name");
query.setHighlightSimplePre("[[");
query.setHighlightSimplePost("]]");
QueryResponse response = client.query(query);
Map<String, Map<String, List<String>>> highlighting =
response.getHighlighting();
for (SolrDocument doc : response.getResults()) {
System.out.println(doc.getFieldValue("id"));
System.out.println(doc.getFieldValue("book_name"));
Map<String, List<String>> perDocument = highlighting == null
? null : highlighting.get(String.valueOf(doc.getFieldValue("id")));
List<String> fragments = perDocument == null
? null : perDocument.get("book_name");
if (fragments != null && !fragments.isEmpty()) {
System.out.println(fragments.get(0));
}
}
}
public static void main(String[] args) throws Exception {
// 只读入口;示意域名需换成自己的隔离测试 core。
String url = "http://solr.example.invalid:8983/solr/demo_core";
try (SolrClient client = new HttpSolrClient.Builder(url).build()) {
query(client);
}
}
}
main 只展示查询;add、replace 和 deleteDemo 是分别展示的变更方法,不会随 main 自动执行。方法包含 commit 便于解释可见性,生产吞吐场景应另设计提交策略,而不是每条记录硬提交。
同 ID 写入的边界
在正常唯一键与覆盖写入语义下,再 add 相同 ID 是整份文档替换,不是“只更新传入字段”。因此 replace 示例提供该演示文档的完整业务字段;若希望局部修改,应使用相应原子更新语法并满足 schema 条件。Solr 更新处理器
deleteDemo 只针对固定演示 ID,不使用全量查询删除。数据写入成功、提交完成和搜索可见,也应分别理解。
查询和高亮常见误区
不要先 set q=:,再 setQuery 另一个条件,却以为两个 q 会自动 AND;后设置会替换前者。多个 fq 使用追加入口,见 过滤条件覆盖问题。
fl 选出的字段要与随后读取的一致。高亮可能缺失,不能直接连续 map.get(…).get(0)。示例输出的是文本标记,不是让页面无条件渲染搜索返回的 HTML。
这里明确使用 SolrJ 8.0 API,不凭旧教程差异推定 SolrServer 究竟在哪个版本移除,也不把版本数字相同当作客户端与服务端已完成兼容性验证。