目录
1 需求分析
2 需求实现
2.1 pom.xml 依赖
2.2 Java 代码示例
3 补充说明
3.1 关于文档中_version_的取值说明
3.2 store=true/false 的区别
1 需求分析
(1) 需求:
向 Solr 中的文档添加新的字段并赋值, 或者修改已有的字段, 对不修改的要保持原值, 也就是不能进行完全覆盖操作.
(2) 前提:
添加的字段 (field) 要提前在 schema.xml 文件中定义, 否则 Solr 无法处理这些字段, 肯定会导致添加失败.
关于 schema.xml 文件的配置, 可参考: Solr 的 schema.xml 模式文件解读 (Solr 的模式设计与优化)
(3) 分析: 我们可以使用 Solr 提供的原子更新, 来实现相关需求:
Solr 支持的原子更新:
set: 修改指定文档中该 field 的值, 如果这个 field 已经存在, 则更新, 如果不存在, 则追加到这个文档中 -- 可以是单值, 也可以是 multi-valued;
add: 向指定文档中的 field 字段添加值, 这个 field 必须是 multi-valued 类型的, 否则将出错 -- 只能是 multi-valued;
inc: 对指定文档中数值类型的值进行自增操作 -- 只能是数值类型, 包括 int,long,float,double.
2 需求实现
2.1 pom.xml 依赖
- <!-- 项目较早, 使用的是 4.10.4 版本的 Solr -->
- <dependency>
- <groupId>org.apache.Solr</groupId>
- <artifactId>Solr-solrj</artifactId>
- <version>4.10.4</version>
- </dependency>
2.2 Java 代码示例
(1) 先获取 Solr 连接:
- String zkHost= "ip:port,ip:port,ip:port";
- // 扩大并发连接数
- ModifiableSolrParams params = new ModifiableSolrParams();
- params.set(HttpClientUtil.PROP_MAX_CONNECTIONS, 1000);
- params.set(HttpClientUtil.PROP_MAX_CONNECTIONS_PER_HOST, 100);
- HttpClient client = HttpClientUtil.createClient(params);
- LBHttpSolrServer lbServer = new LBHttpSolrServer(client);
- CloudSolrServer solrServer = new CloudSolrServer(zkHost, lbServer);
- // 为 Solr 连接设置默认的 Collection
- solrServer.setDefaultCollection("C_Book");
- // 设置 ZooKeeper 连接超时时间
- solrServer.setZkClientTimeout(18000);
- solrServer.setZkConnectTimeout(36000);
(2) 准备需要处理的 Solr 文档, 相关注意事项已经在代码注释中作了详细说明:
- // 为了提高效率, 可以使用批量操作
- Collection<SolrInputDocument> updateDocList = new ArrayList<>();
- for (int i = 0; i <5; ++i) {
- SolrInputDocument doc = new SolrInputDocument();
- // 局部更新需要指定文档的 id(在 schema.xml 中配置的主键),
- // 主键不需要添加 set,add 等信息, 其他需要原子更新的 field 需要构造为 Map
- doc.addField("id", i);
- // 局部更新需要借助 Map, 这个 Map 的 Key 必须是 "set"
- Map<String, String> publisherMap = new HashMap<>();
- publisherMap.put("set", "人民邮电出版社");
- // 修改图书的出版社, key 是 field, value 是上述的 Map
- doc.addField("publisher", publisherMap);
- // 在已有仓库的基础上, 再添加多个仓库, 注意: 此 field 必须是 multi-valued 类型
- Map<String, List<String>> stockCityMap = new HashMap<>();
- List<String> list = new ArrayList();
- list.add("广州");
- list.add("深圳");
- // 局部添加需要借助 Map, 这个 Map 的 Key 必须是 "add"
- stockCityMap.put("add", list);
- // 修改图书的仓库城市, key 是 field, value 是上述的 Map
- doc.addField("stockCity", stockCityMap);
- // 在已有图书价格的基础上: 每本增加 9.50 元, 注意: 此 field 必须是数值类型
- Map<String, Long> priceMap = new HashMap<>();
- // 局部自增需要借助 Map, 这个 Map 的 Key 必须是 "inc"
- priceMap.put("inc", 9.50L);
- // 修改图书的价格, key 是 field, value 是上述的 Map
- doc.addField("price", priceMap);
- // _version_值为 0: 如果待修改的文档存在, 则修改; 如果不存在, 则添加
- doc.addField("_version_", 0);
- updateDocList.add(doc);
- }
(3) 向 SolrCloud 中提交批量添加请求:
- // 连接 SolrCloud
- solrServer.connect();
- // 添加提交文档 List
- UpdateResponse rsp = solrServer.add(updateDocList);
- System.out.println("操作状态:" + rsp.getStatus() + ", 操作时间:" + rsp.getQTime());
- // 提交策略: 不用手动提交, 交由 Solr 服务根据配置自动进行软提交;
- // 如果要手动提交, 不要使用无参方法, 推荐指定提交策略: 是否等待刷新(建议不等待: 会阻塞), 等待可搜索(建议不等待: 会阻塞), 软提交
- UpdateResponse rspCommit = solrServer.commit(false, false, true);
- System.out.println("提交状态:" + "result:" + rspCommit.getStatus() + ", 操作时间:" + rspCommit.getQTime());
3 补充说明
3.1 关于文档中_version_的取值说明
(1) version <0: 如果待修改的文档存在, Solr 会拒绝修改; 如果不存在, 就添加这个文档.
(2) version = 0: 如果待修改的文档存在, 就更新这个文档; 如果不存在, 就添加这个文档.
(3) version = 1: 如果待修改待文档存在, 就更新这个文档; 如果不存在, Solr 会拒绝修改它, 并抛出类似的错误信息:
version conflict for 1 expected=1 actual=-1
(4) version> 1: 如果文档的_version_值和传入的_version_值不同, Solr 就会拒绝修改; 值相同时才执行修改.
3.2 store=true/false 的区别
(1) 如果某个字段在 schema.xml 中指定了 store=false, 那么即使这个字段有值, 在更新的时候也会被 Solr 丢弃, 而指定为 store=true 的字段则不会;
(2) 对于 multi-field(多值)字段, 如果指定其 store=false, 则在原子更新使用 add 的时候会级联清除该字段之前的数据.
参考资料
Solr 的原子更新 / 局部更新
来源: https://www.cnblogs.com/shoufeng/p/11124286.html