Custom SOLR Search Components - 2 Dev Tricks
I've been building some custom search components for SOLR lately, so wanted to share a couple of things I learned in the process. Most likely this is old hat to people who have been doing this for a while, but thought I'd share, just in case it benefits someone...
Passing State
In a previous post, I described a custom SOLR search handler returns layered search results for a given query term (and optional filters). As I went further, though, I realized that I needed to return information relating to facets and category clusters as well. Of course, I could have added this stuff into the handler itself, but splitting the logic across a chain of search components seemed to be more preferable, readability and reusability wise, so I went that route.
So the first step was to refactor my custom SearchHandler into a SearchComponent. Not much to do there, except to subclass SearchComponent instead of RequestHandlerBase and move the handleRequestBody(SolrQueryRequest,SolrQueryResponse) to a process(ResponseBuilder) method. The request and response objects are accessible from the ResponseBuilder as properties, ie, ResponseBuilder.req and ResponseBuilder.rsp. I then declared this component and an enclosing handler in solrconfig.xml, something like this:
1 |
<!-- this used to be my search handler --> |
I've also added a second component to the chain above (just so I don't have to show this snippet again later), hope its not too confusing. Obviously there can be multiple components before and after my search handler turned search component, but for the purposes of this discussion, I'll keep things simple and just concentrate on this one other component and pretend that it has multiple unique (and pertinent) requirements.
Now, assume that the second component needed data that was already available, or can be easily generated by component1. Its actually true in my case, since I needed a BitSet of document ids in the search results in my second component, which I could easily get by collecting them while looping through the SolrDocumentList of results in my first component. So it seemed kind of wasteful to compute this again. So I updated this snippet of code in component1's process() method (what used to be my handleRequestBody() method):
1 |
public void process(ResponseBuilder rb) throws IOException { |
In my next component (component2), I simply grab the OpenBitSet data structure by name from the NamedList, use them to generate the result for this component, stick the result back into the response, and discard the temporary data. The last is so that the data does not appear on the response XML (for both aesthetic and performance reasons).
1 |
public void process(ResponseBuilder rb) throws IOException { |
Before I did this, I investigated if I could subclass the XmlResponseWriter to ignore NamedLists with "hidden" names (ie names prefixed with underscore), but the XmlResponseWriter calls XMLWriter which does the actual XML generation, and XMLWriter is final (at least in SOLR 1.4.1). Good thing too, forced me to look for and find a simpler solution :-).
So there you have it - a simple way to pass data between components in a SOLR Search RequestHandler. Note that it does mean that component2 is always dependent on component1 (or some other component that produces the same data) upstream to it, so these components are no longer truly reusable pieces of code. But this can be useful if you really need it and you document the requirement (or complain about it if not met, as I've done here).
Reacting to a COMMIT
The second thing I needed to do in component2 was to give it some reference data that it would need to compute its results. The reference data is generated from the contents of the index, and the generation is fairly heavyweight, so you don't want to do this on every request.
Now one of the cool things about SOLR is its built-in incremental indexing feature (one of the main reasons we considered using SOLR in the first place), so you can POST data to a running SOLR instance followed by a COMMIT, and voila: your searcher re-opens with the new data.
Of course, this also means that if we want to provide accurate information, the reference data should be regenerated whenever the searcher is reopened. The way I went about doing this is mostly derived from how the SpellCheckerComponent does it, in order to regenerate its dictionaries -- by hooking into the SOLR event framework.
To do this, my component2 implements SolrCoreAware in addition to extending SearchComponent. This requires me to implement the inform(SolrCore) method, which is invoked by SOLR after the init(NamedList) but before prepare(ResponseBuilder) and process(ResponseBuilder). In the inform(SolrCore) method, I register a listener for the firstSearcher and newSearcher events (described in more detail here).
I then build the inner listener class, which implements SolrEventListener, which requires me to provide implementations for newSearcher() and postCommit() methods. Since my listener is a query-side listener, I provide an empty implementation for postCommit(). The newSearcher() method contains the code to generate the reference sets. Here is the relevant snippet of code from the component.
1 |
public class MyComponent2 extends SearchComponent implements SolrCoreAware { private RefData refdata; // this needs to be regenerated on COMMIT @Override |
Notice that I have registered the listener to listen on both firstSearcher and newSearcher events. This way, it gets called on SOLR startup (reacting to a firstSearcher event), and again each time the searcher is reopened (reacting to a newSearcher event).
One other thing... since the generation of RefData takes some time, its best to have the listener's newSearcher method build a copy and then repopulate the refdata variable from the copy, that way the component continues to use the old data until the new one is available.
And thats pretty much it for today. Till next time.
http://sujitpal.blogspot.com/2011/04/custom-solr-search-components-2-dev.html
Custom SOLR Search Components - 2 Dev Tricks的更多相关文章
- How to Reuse Old PCs for Solr Search Platform?
家裡或公司的舊電腦不夠力? 效能慢到想砸爛它們? 朋友或同事有電腦要丟嗎? 我有一個廢物利用的方法, 我收集了四台舊電腦, 組了一個Fully Distributed Mode的Hadoop Clus ...
- solr search基础知识(控制符及其参数)
1.^ 控制符 (1)查询串上用^ 搜索: 天后王菲,如果希望将王菲的相关度加大,用^控制符. 天后 王菲^10.5 结果就会将含有王菲的document权重加大分数提高,排序靠前,10.5为权重 ...
- Solr Cloud - SolrCloud
关于 Solr Cloud Zookeeper 入门,介绍 原理 原封不动转自 http://wiki.apache.org/solr/SolrCloud/ ,文章的内存有些过时,但是了解原理. Th ...
- Solr: a custom Search RequestHandler
As you know, I've been playing with Solr lately, trying to see how feasible it would be to customize ...
- Importing/Indexing database (MySQL or SQL Server) in Solr using Data Import Handler--转载
原文地址:https://gist.github.com/maxivak/3e3ee1fca32f3949f052 Install Solr download and install Solr fro ...
- Solr 6.7学习笔记(03)-- 样例配置文件 solrconfig.xml
位于:${solr.home}\example\techproducts\solr\techproducts\conf\solrconfig.xml <?xml version="1. ...
- Solr基础知识二(导入数据)
上一篇讲述了solr的安装启动过程,这一篇讲述如何导入数据到solr里. 一.准备数据 1.1 学生相关表 创建学生表.学生专业关联表.专业表.学生行业关联表.行业表.基础信息表,并创建一条小白的信息 ...
- Solr 02 - 最详细的solrconfig.xml配置文件解读
目录 1 luceneMatchVersion - 指定Lucene版本 2 lib - 配置扩展jar包 3 dataDir - 索引数据路径 4 directoryFactory - 索引存储工厂 ...
- solr 5.3.1安装配置
1.下载Solr5.3.1 http://mirror.bit.edu.cn/apache/lucene/solr/5.3.1/ wget http://mirror.bit.edu.cn/apach ...
随机推荐
- 【转】Android Eclipse调试技巧
原文地址:https://www.cnblogs.com/tianchunming/p/5423942.html Android Eclipse调试技巧 在Android 应用程序开发中我们经常需 ...
- day-9心得
操作系统发展史 手工操作(无操作系统) 1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统,计算机工作采用手工操作方式. 手工操作程序员将对应于程序和数据的已穿孔的纸带(或卡片)装入 ...
- new及placememt new 异同点
new与定位new 区别如下: 简单概括: new 分配的内存地址空间来自于heap堆,用完需使用delete 释放内存 定位new 使用的不是heap堆内存,因此不需要使用delete 释放 定位n ...
- G++ 4.4.7 无法编译模板程序,Vs可以,和解?智者尾部留言,本人第一次使用vs pro,通常并且习惯在linux下写些小东西,虽然程序简单;
vs 模板编译运行Ok \ linux g++ 4.4.7编译模板测试程序,报无法定义 template <typename or class 中的 AnyType> 类型的数据 Exam ...
- JQueryDOM节点操作
你一.JQueryDom节点操作 2.1查找节点 获取p节点 var $pDm=$("p"); 输出p节点的title属性 alert($pDm.attr("title& ...
- adb命令检测apk启动时间、内存、CPU使用情况、流量、电池电量等——常用的adb命令
ADB:Android Debug Bridge,是Android SDK里一个可以直接操作安卓模拟器或真实设备的工具,颇为强大. 检测APP: adb shell am start -W p ...
- ajax 回调函数
回调函数 如果要处理$.ajax()得到的数据,则需要使用回调函数.beforeSend.error.dataFilter.success.complete. beforeSend 在发送请求之前调用 ...
- inl文件介绍
inl文件是内联函数的源文件.内联函数通常在C++头文件中实现,但是当C++头文件中内联函数过多的情况下,我们想使头文件看起来简洁点,能不能像普通函数那样将内联函数声明和函数定义放在头文件和实现文件中 ...
- 深入探究jvm之类装载器
一.class装载验证流程 1.加载 1).取得类的二进制流. 2).转为方法区数据结构. 3).在Java堆中生成对应的java.lang.Class对象. 2.链接--验证(目的:保证Class流 ...
- C# 对象封装为json格式
1 对象 public class Person { public string Name { get; set; } public int Age { get; set; } public Date ...