Index: lucene/highlighter/src/test/org/apache/lucene/search/postingshighlight/TestPostingsHighlighter.java =================================================================== --- lucene/highlighter/src/test/org/apache/lucene/search/postingshighlight/TestPostingsHighlighter.java (revision 1458000) +++ lucene/highlighter/src/test/org/apache/lucene/search/postingshighlight/TestPostingsHighlighter.java (working copy) @@ -39,6 +39,7 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.Sort; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TopDocs; @@ -465,4 +466,42 @@ ir.close(); dir.close(); } + + public void testSpecificDocIDs() throws Exception { + Directory dir = newDirectory(); + IndexWriterConfig iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random())); + iwc.setMergePolicy(newLogMergePolicy()); + RandomIndexWriter iw = new RandomIndexWriter(random(), dir, iwc); + + FieldType offsetsType = new FieldType(TextField.TYPE_STORED); + offsetsType.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS); + Field body = new Field("body", "", offsetsType); + Document doc = new Document(); + doc.add(body); + + body.setStringValue("This is a test. Just a test highlighting from postings. Feel free to ignore."); + iw.addDocument(doc); + body.setStringValue("Highlighting the first term. Hope it works."); + iw.addDocument(doc); + + IndexReader ir = iw.getReader(); + iw.close(); + + IndexSearcher searcher = newSearcher(ir); + PostingsHighlighter highlighter = new PostingsHighlighter(); + Query query = new TermQuery(new Term("body", "highlighting")); + TopDocs topDocs = searcher.search(query, null, 10, Sort.INDEXORDER); + assertEquals(2, topDocs.totalHits); + ScoreDoc[] hits = topDocs.scoreDocs; + int[] docIDs = new int[2]; + docIDs[0] = hits[0].doc; + docIDs[1] = hits[1].doc; + String snippets[] = highlighter.highlightFields(new String[] {"body"}, query, searcher, docIDs, 1).get("body"); + assertEquals(2, snippets.length); + assertEquals("Just a test highlighting from postings. ", snippets[0]); + assertEquals("Highlighting the first term. ", snippets[1]); + + ir.close(); + dir.close(); + } } Index: lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java =================================================================== --- lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java (revision 1458000) +++ lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java (working copy) @@ -237,16 +237,39 @@ * {@link IndexOptions#DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS} */ public Map highlightFields(String fields[], Query query, IndexSearcher searcher, TopDocs topDocs, int maxPassages) throws IOException { + final ScoreDoc scoreDocs[] = topDocs.scoreDocs; + int docids[] = new int[scoreDocs.length]; + for (int i = 0; i < docids.length; i++) { + docids[i] = scoreDocs[i].doc; + } + + return highlightFields(fields, query, searcher, docids, maxPassages); + } + + /** + * Highlights the top-N passages from multiple fields, + * for the provided int[] docids. + * + * @param fields field names to highlight. + * Must have a stored string value and also be indexed with offsets. + * @param query query to highlight. + * @param searcher searcher that was previously used to execute the query. + * @param docids containing the document IDs to highlight. + * @param maxPassages The maximum number of top-N ranked passages per-field used to + * form the highlighted snippets. + * @return Map keyed on field name, containing the array of formatted snippets + * corresponding to the documents in topDocs. + * If no highlights were found for a document, its value is null. + * @throws IOException if an I/O error occurred during processing + * @throws IllegalArgumentException if field was indexed without + * {@link IndexOptions#DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS} + */ + public Map highlightFields(String fields[], Query query, IndexSearcher searcher, int[] docids, int maxPassages) throws IOException { final IndexReader reader = searcher.getIndexReader(); - final ScoreDoc scoreDocs[] = topDocs.scoreDocs; query = rewrite(query); SortedSet queryTerms = new TreeSet(); query.extractTerms(queryTerms); - int docids[] = new int[scoreDocs.length]; - for (int i = 0; i < docids.length; i++) { - docids[i] = scoreDocs[i].doc; - } IndexReaderContext readerContext = reader.getContext(); List leaves = readerContext.leaves(); @@ -277,15 +300,15 @@ Term terms[] = fieldTerms.toArray(new Term[fieldTerms.size()]); Map fieldHighlights = highlightField(field, contents[i], bi, terms, docids, leaves, maxPassages); - String[] result = new String[scoreDocs.length]; - for (int j = 0; j < scoreDocs.length; j++) { - result[j] = fieldHighlights.get(scoreDocs[j].doc); + String[] result = new String[docids.length]; + for (int j = 0; j < docids.length; j++) { + result[j] = fieldHighlights.get(docids[j]); } highlights.put(field, result); } return highlights; } - + private Map highlightField(String field, String contents[], BreakIterator bi, Term terms[], int[] docids, List leaves, int maxPassages) throws IOException { Map highlights = new HashMap(); Index: solr/core/src/java/org/apache/solr/highlight/PostingsSolrHighlighter.java =================================================================== --- solr/core/src/java/org/apache/solr/highlight/PostingsSolrHighlighter.java (revision 1458000) +++ solr/core/src/java/org/apache/solr/highlight/PostingsSolrHighlighter.java (working copy) @@ -129,16 +129,16 @@ // if highlighting isnt enabled, then why call doHighlighting? if (isHighlightingEnabled(params)) { SolrIndexSearcher searcher = req.getSearcher(); - TopDocs topDocs = toTopDocs(docs); + int[] docIDs = toDocIDs(docs); // fetch the unique keys - String[] keys = getUniqueKeys(searcher, topDocs); + String[] keys = getUniqueKeys(searcher, docIDs); // query-time parameters String[] fieldNames = getHighlightFields(query, req, defaultFields); int numSnippets = params.getInt(HighlightParams.SNIPPETS, 1); - Map snippets = highlighter.highlightFields(fieldNames, query, searcher, topDocs, numSnippets); + Map snippets = highlighter.highlightFields(fieldNames, query, searcher, docIDs, numSnippets); return encodeSnippets(keys, fieldNames, snippets); } else { return null; @@ -171,38 +171,38 @@ return list; } - /** Converts solr's DocList to a lucene TopDocs */ - protected TopDocs toTopDocs(DocList docs) { - ScoreDoc[] scoreDocs = new ScoreDoc[docs.size()]; + /** Converts solr's DocList to the int[] docIDs */ + protected int[] toDocIDs(DocList docs) { + int[] docIDs = new int[docs.size()]; DocIterator iterator = docs.iterator(); - for (int i = 0; i < scoreDocs.length; i++) { + for (int i = 0; i < docIDs.length; i++) { if (!iterator.hasNext()) { throw new AssertionError(); } - scoreDocs[i] = new ScoreDoc(iterator.nextDoc(), Float.NaN); + docIDs[i] = iterator.nextDoc(); } if (iterator.hasNext()) { throw new AssertionError(); } - return new TopDocs(docs.matches(), scoreDocs, Float.NaN); + return docIDs; } /** Retrieves the unique keys for the topdocs to key the results */ - protected String[] getUniqueKeys(SolrIndexSearcher searcher, TopDocs topDocs) throws IOException { + protected String[] getUniqueKeys(SolrIndexSearcher searcher, int[] docIDs) throws IOException { IndexSchema schema = searcher.getSchema(); SchemaField keyField = schema.getUniqueKeyField(); if (keyField != null) { Set selector = Collections.singleton(keyField.getName()); - String uniqueKeys[] = new String[topDocs.scoreDocs.length]; - for (int i = 0; i < topDocs.scoreDocs.length; i++) { - int docid = topDocs.scoreDocs[i].doc; + String uniqueKeys[] = new String[docIDs.length]; + for (int i = 0; i < docIDs.length; i++) { + int docid = docIDs[i]; StoredDocument doc = searcher.doc(docid, selector); String id = schema.printableUniqueKey(doc); uniqueKeys[i] = id; } return uniqueKeys; } else { - return new String[topDocs.scoreDocs.length]; + return new String[docIDs.length]; } } }