处理搜索结果

查询调用在正常完成后会以 Results 对象形式返回结果。Results 对象告诉您在索引中找到了多少个匹配文档,以及返回了多少个匹配的文档。此外,该对象还包含一个匹配的 ScoredDocuments 对象集合。该集合通常包含找到的所有匹配文档的一部分,因为每次调用时搜索都只返回有限数量的文档。通过使用偏移或游标,可以检索所有匹配文档,每次检索一个子集。

结果

// index and query have already been defined ...
try {
    Results<ScoredDocument> result = index.search(query);
    long totalMatches = result.getNumberFound();
    int numberOfDocsReturned = result.getNumberReturned();
    Collection<ScoredDocument> listOfDocs = result.getResults();
} catch (SearchException e) {
    // handle exception...
}

根据 limit 查询选项的值,结果中返回的匹配文档数可能小于找到的数量。请注意,如果 NumberFoundAccuracy 属性的值小于找到的数量,getNumberFound 方法返回的将是估算值。无论如何配置搜索选项,search() 调用找到的匹配文档数都不会超过 10000 个。

如果找到的文档多于返回的文档,并且您想要检索所有找到的文档,则需要使用偏移或游标来重复搜索,如下所述。

得分文档

搜索结果将包含一个与查询匹配的 ScoredDocuments 集合。您可以使用 getResults() 方法检索该集合,或直接从搜索结果本身遍历集合中的成员。下面显示了这两种方法:

// index and query have already been defined ...
Results<ScoredDocument> result = index.search(query);

// Grab the collection for later use:
Collection<ScoredDocument> theDocs = result.getResults();

// Alternatively, iterate over the results directly:
for (ScoredDocument doc : result) {
    // do work
}

默认情况下,得分文档包含编入索引的原始文档的所有字段。如果查询选项指定了 setFieldsToReturn,则对文档调用 getFields() 时,只有指定的字段会出现在结果中。如果使用 addExpressionToReturnsetFieldsToSnippet 创建了计算字段,可通过对文档调用 getExpressions() 来单独检索它们。

使用偏移

如果搜索找到的文档多于可以一次返回的文档,请使用偏移将其编入匹配文档列表的索引中。例如,默认查询限制为 20 个文档。首次执行搜索(offset 为 0)并检索到前 20 个文档后,在检索接下来的 20 个文档时,请将 offset 设置为 20 并再次运行相同的搜索。继续重复搜索,根据返回的文档数逐次增加偏移量:

// index and queryString have already been defined

try {
    int numberRetrieved = 0;
    int offset = 0;
    do {
        // build options and query
        QueryOptions options = QueryOptions.newBuilder()
            .setOffset(offset)
            .build();
        Query query = Query.newBuilder().setOptions(options).build(queryString);

        // search at least once
        Results<ScoredDocument> result = index.search(query);
        numberRetrieved = result.getNumberReturned();
        if (numberRetrieved > 0) {
            offset += numberRetrieved;
            // process the matched docs
        }

    }
    while (numberRetrieved > 0);
} catch (SearchException e) {
    // handle exception...
}

遍历大型结果集时,偏移的效率很低。

使用游标

您还可以使用游标来检索结果的子范围。如果您打算在连续页面中显示搜索结果,并且希望确保在不同查询之间索引可能会有改动的情况下不跳过任何文档,游标很有用。遍历大型结果集时,游标也更高效。

如需使用游标,必须创建一个初始游标并将其包含在查询选项中。游标有两种:per-query 和 per-result。per-query 游标生成一个将与搜索调用返回的结果对象相关的单独游标。per-result 游标生成一个将与结果中的每个得分文档相关的游标。

使用 per-query 游标

默认情况下,新构造的游标是 per-query 游标。此游标保存搜索结果中返回的最后一个文档的位置。每次搜索都会更新此游标。如需枚举索引中的所有匹配文档,请执行相同的搜索,直到结果返回空游标:

// index and queryString have already been defined

try {
    // create the initial cursor
    Cursor cursor = Cursor.newBuilder().build();

    do {
        // build options and query
        QueryOptions options = QueryOptions.newBuilder()
            .setCursor(cursor)
            .build();
        Query query = Query.newBuilder().setOptions(options).build(queryString);

        // search at least once
        Results<ScoredDocument> result = index.search(query);
        int numberRetrieved = result.getNumberReturned();
        cursor = result.getCursor();

        if (numberRetrieved > 0) {
            // process the matched docs
        }

    }
    while (cursor != null);
    // all done!
} catch (SearchException e) {
    // handle exception...
}

使用 per-result 游标

如需创建 per-result 游标,必须在创建初始游标时将游标的 perResult 属性设置为 true。当搜索返回时,每个文档都将有一个与之相关的游标。可以使用该游标来指定新搜索的结果以特定文档开头。请注意,将 per-result 游标传递给搜索时,将不会有 per-query 游标与搜索结果本身相关;result.getCursor() 将返回 null,因此您无法使用该方法来测试是否已检索到所有匹配项。

// index and queryString have already been defined

try {
    // create an initial per-result cursor
    Cursor cursor = Cursor.newBuilder().setPerResult(true).build();
    // build options and query
    QueryOptions options = QueryOptions.newBuilder()
        .setCursor(cursor)
        .build();
    Query query = Query.newBuilder().setOptions(options).build(queryString);
    Results<ScoredDocument> result = index.search(query);

    // process the matched docs
    cursor = null;
    for (ScoredDocument doc : result) {
        // discover some document of interest and grab its cursor
        if (...)
            cursor = doc.getCursor();
     }

    // Start the next search from the document of interest
    if (cursor != null) {
        options = QueryOptions.newBuilder()
            .setCursor(cursor)
            .build();
        query = Query.newBuilder().setOptions(options).build(queryString);
        result = index.search(query);
    }
} catch (SearchException e) {
    // handle exception
}

保存和恢复游标

您可以将游标序列化为一个 Web 安全字符串,然后进行保护和恢复以供日后使用:

String cursorString = cursor.toWebSafeString();
// Save the string ... and restore:
Cursor cursor = Cursor.newBuilder().build(cursorString));