1

我想让 Solr 始终检索通过搜索找到的所有文档(我知道 Solr 不是为此而构建的,但无论如何),我目前正在使用以下代码执行此操作:

    ...
    List<Article> ret = new ArrayList<Article>();
    QueryResponse response = solr.query(query);
    int offset = 0;
    int totalResults = (int) response.getResults().getNumFound();
    List<Article> ret = new ArrayList<Article>((int) totalResults);
    query.setRows(FETCH_SIZE);
    while(offset < totalResults) {
        //requires an int? wtf?
        query.setStart((int) offset);
        int left = totalResults - offset;
        if(left < FETCH_SIZE) {
            query.setRows(left);
        }
        response = solr.query(query);
        List<Article> current = response.getBeans(Article.class);
        offset += current.size();
        ret.addAll(current);
    }
   ...

这可行,但如果查询超过 1000 次点击,则速度会很慢(我在这里读到过。这是由 Solr 引起的,因为我每次都在设置开始 - 出于某种原因 - 需要一些时间)。有什么更好(更快)的方法来做到这一点?

4

3 回答 3

8

要改进建议的答案,您可以使用流式响应。这是特别为获取所有结果的情况而添加的。正如你在 Solr 的 Jira 中看到的那样,那个人想做和你一样的事情。这已在 Solr 4 中实现。

这也在 Solrj 的 javadoc 中有所描述

Solr 将在开始发送响应之前打包响应并创建一个完整的 XML/JSON 文档。然后,您的客户需要解开所有内容并将其作为列表提供给您。通过使用流式处理和并行处理(在使用这种排队方法时可以这样做),性能应该会进一步提高。

是的,您将失去自动 bean 映射,但由于性能是这里的一个因素,我认为这是可以接受的。

这是一个示例单元测试:

public class StreamingTest {

  @Test
  public void streaming() throws SolrServerException, IOException, InterruptedException {
    HttpSolrServer server = new HttpSolrServer("http://your-server");
    SolrQuery tmpQuery = new SolrQuery("your query");
    tmpQuery.setRows(Integer.MAX_VALUE);
    final BlockingQueue<SolrDocument> tmpQueue = new LinkedBlockingQueue<SolrDocument>();
    server.queryAndStreamResponse(tmpQuery, new MyCallbackHander(tmpQueue));
    SolrDocument tmpDoc;
    do {
      tmpDoc = tmpQueue.take();
    } while (!(tmpDoc instanceof PoisonDoc));
  }

  private class PoisonDoc extends SolrDocument {
    // marker to finish queuing
  }

  private class MyCallbackHander extends StreamingResponseCallback {
    private BlockingQueue<SolrDocument> queue;
    private long currentPosition;
    private long numFound;

    public MyCallbackHander(BlockingQueue<SolrDocument> aQueue) {
      queue = aQueue;
    }

    @Override
    public void streamDocListInfo(long aNumFound, long aStart, Float aMaxScore) {
      // called before start of streaming
      // probably use for some statistics
      currentPosition = aStart;
      numFound = aNumFound;
      if (numFound == 0) {
        queue.add(new PoisonDoc());
      }
    }

    @Override
    public void streamSolrDocument(SolrDocument aDoc) {
      currentPosition++;
      System.out.println("adding doc " + currentPosition + " of " + numFound);
      queue.add(aDoc);
      if (currentPosition == numFound) {
        queue.add(new PoisonDoc());
      }
    }
  }
}
于 2013-04-04T11:45:52.157 回答
1

您可以通过增加FETCH_SIZE. 因为你得到了所有的结果,所以除非你关心内存或类似的东西,否则分页是没有意义的。如果 1000 个结果容易导致内存溢出,我会说你目前的表现似乎相当出色。

因此,我会尝试一次获取所有内容,并将其简化为:

//WHOLE_BUNCHES is a constant representing a reasonable max number of docs we want to pull here.
//Integer.MAX_VALUE would probably invite an OutOfMemoryError, but that would be true of the
//implementation in the question anyway, since they were still being stored in the list at the end.
query.setRows(WHOLE_BUNCHES);
QueryResponse response = solr.query(query);
int totalResults = (int) response.getResults().getNumFound(); //If you even still need this figure.
List<Article> ret = response.getBeans(Article.class);

如果您需要保留分页:

您正在执行第一个查询:

QueryResponse response = solr.query(query);

并从中填充找到的结果的数量,但您没有在响应中提取任何结果。即使你在这里保持分页,你至少可以在这里消除一个额外的查询。

这:

int left = totalResults - offset;
if(left < FETCH_SIZE) {
    query.setRows(left);
}

是不必要的。 setRows指定要返回的最大行数,因此要求超过可用行数不会导致任何问题。

最后,什么都没有,但我不得不问:setStart如果不是,你会期望采取什么论据int

于 2013-04-02T16:37:58.307 回答
0

使用以下逻辑批量获取 solr 数据以优化 solr 数据获取查询的性能:

public List<Map<String, Object>> getData(int id,Set<String> fields){
        final int SOLR_QUERY_MAX_ROWS = 3;
        long start = System.currentTimeMillis();
        SolrQuery query = new SolrQuery();
        String queryStr = "id:" + id;
        LOG.info(queryStr);
        query.setQuery(queryStr);
        query.setRows(SOLR_QUERY_MAX_ROWS);
        QueryResponse rsp = server.query(query, SolrRequest.METHOD.POST);
        List<Map<String, Object>> mapList = null;
        if (rsp != null) {
            long total = rsp.getResults().getNumFound();
            System.out.println("Total count found: " + total);
            // Solr query batch
            mapList = new ArrayList<Map<String, Object>>();
            if (total <= SOLR_QUERY_MAX_ROWS) {
                addAllData(mapList, rsp,fields);
            } else {
                int marker = SOLR_QUERY_MAX_ROWS;
                do {
                    if (rsp != null) {
                        addAllData(mapList, rsp,fields);
                    }
                    query.setStart(marker);
                    rsp = server.query(query, SolrRequest.METHOD.POST);
                    marker = marker + SOLR_QUERY_MAX_ROWS;
                } while (marker <= total);
            }
        }

        long end = System.currentTimeMillis();
        LOG.debug("SOLR Performance: getData: " + (end - start));

        return mapList;
    }

private void addAllData(List<Map<String, Object>> mapList, QueryResponse rsp,Set<String> fields) {
            for (SolrDocument sdoc : rsp.getResults()) {
                Map<String, Object> map = new HashMap<String, Object>();
            for (String field : fields) {
                map.put(field, sdoc.getFieldValue(field));
            }
            mapList.add(map);
        }
    }
于 2017-02-20T08:51:18.057 回答