2

我正在编写一个自定义 Solr 组件。在组件的 prepare 方法中,我正在执行一个作为自定义参数(在内部req.params)给出的查询。我没有q在 prepare 方法中运行参数查询,而是在自定义参数中定义的另一个输入查询。我正在使用该自定义输入查询返回的文档在 prepare 方法中做一些准备工作。

问题是,由于我的索引分布在多个分片中,自定义查询返回的文档只是位于其中一个分片上的文档。换句话说,在我的准备方法中执行的搜索没有分发,我得到了部分结果。这或多或少是我在准备方法中执行搜索的方式:

rb.req.getSearcher().getDocList(customQuery, null, null, offset, len, 0);

有没有办法在 prepare 方法中进行分布式搜索并从所有分片中获取匹配的文档?


编辑:

我目前的解决方案是使用 Solrj 执行查询,大致如下:

SolrServer server = new HttpSolrServer(url);
SolrQuery request = new SolrQuery(customQuery);
NamedList queryResponse = server.query(request).getResponse();

然后我解析响应以获取返回文档的内容。我不喜欢我的解决方案有几个原因。原因之一是我必须解析响应。但主要原因是我必须将 Solr 服务器 url 作为参数传递。我把网址放在solrconfig.xml文件中。是否可以在不明确说明 Solr 服务器 url(可能通过 ZooKeeper)的情况下以某种方式构造 SolrServer 实例?

4

1 回答 1

4

简单的方法

使用CloudSolrServer执行分布式查询。向它提供 Zookeeper url 和集合名称(在响应构建器中可用):

CoreDescriptor coreDescriptor = rb.req.getCore().getCoreDescriptor();
String collectionName = coreDescriptor.getCloudDescriptor().getCollectionName();    
ZkController zkController = coreDescriptor.getCoreContainer().getZkController();    
String zookeeperUrl = zkController.getZkServerAddress();

CloudSolrServer server = new CloudSolrServer(zookeeperUrl);
server.setDefaultCollection(collectionName);
server.connect();

SolrRequest request = ... //initialize the solr request to execute the query
NamedList<Object> solrResponse = server.request(solrRequest);
// do whatever you like with the returned response;
server.shutdown();

正确的方式

不要在 prepare 方法中执行分布式搜索。不要在 prepare 方法中查询索引。您要做的是首先决定您希望在哪个执行阶段执行分布式查询。阶段是STAGE_STARTSTAGE_PARSE_QUERYSTAGE_TOP_GROUPSSTAGE_EXECUTE_QUERYSTAGE_GET_FIELDSSTAGE_DONE如果您需要它在两个阶段之间执行,则创建一个新的中间阶段(例如EXECUTE_PREPARING_QUERY)。

覆盖该distributedProcess方法并以这样的方式实现它,如果当前阶段是您的阶段,则为分片请求设置正确的参数:

@Override public int distributedProcess(ResponseBuilder rb) {
    ...
    if (rb.stage == MY_STAGE) {
       ShardRequest sreq = new ShardRequest();
       sreq.purpose = ShardRequest.PURPOSE_PRIVATE;
       sreq.params = new ModifiableSolrParams();
       // set the parameters for the shard request
       rb.addRequest(this, sreq);
    }
    ...
}

现在每个分片都将执行由您在其自己的核心上设置的参数定义的请求。这将发生在舞台上MY_STAGE。您仍然必须处理碎片的响应,将它们组合并使用它们。处理所有这些响应的正确位置是在handleResponses组件的方法中。handleResponses因此,如果您处于正确的阶段,请覆盖并执行您需要对分片响应执行的任何操作。您可能需要将它们保存在某个地方,以便稍后在该finishStage方法中引用它们。

@Override public void handleResponses(ResponseBuilder rb, ShardRequest sreq) {
   ...
   if (stage == MY_STAGE) {
      List<ShardResponse> responses = sreq.responses;
      for (ShardResponse response : responses) {
         //do something with the response, maybe save it somewhere
         rb.finished.remove(sreq);
      }
   }
   ...
}

现在您必须重写该finishStage方法并对组合结果执行您需要做的任何事情。

@Override public void finishStage(ResponseBuilder rb) {
   ...
   if (rb.stage == MY_STAGE) {
      // do whatever you need to do with the results
   }
   ...
}

重要的信息是使用响应构建器阶段来控制组件相对于其他组件的执行流程。如果您希望在执行实际查询之前执行代码,则不必将代码放在 prepare 方法中。您只需要创建或使用一个介于STAGE_START和之间的阶段STAGE_EXECUTE_QUERY

于 2013-12-09T12:27:28.733 回答