diff --git a/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java index e3d07b1cbca7..b5879024d100 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java @@ -472,7 +472,7 @@ private static Document getFirstLiveDoc(Terms terms, LeafReader reader) throws I StoredFields storedFields = reader.storedFields(); // Deal with the chance that the first bunch of terms are in deleted documents. Is there a // better way? - for (int idx = 0; idx < 1000 && postingsEnum == null; ++idx) { + for (int idx = 0; idx < 1000; ++idx) { text = termsEnum.next(); // Ran off the end of the terms enum without finding any live docs with that field in them. if (text == null) { @@ -481,7 +481,7 @@ private static Document getFirstLiveDoc(Terms terms, LeafReader reader) throws I postingsEnum = termsEnum.postings(postingsEnum, PostingsEnum.NONE); final Bits liveDocs = reader.getLiveDocs(); if (postingsEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) { - if (liveDocs != null && liveDocs.get(postingsEnum.docID())) { + if (liveDocs != null && !liveDocs.get(postingsEnum.docID())) { continue; } return storedFields.document(postingsEnum.docID()); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerTest.java index 02d7066d215b..8edb23d11a8b 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/LukeRequestHandlerTest.java @@ -20,6 +20,7 @@ import java.util.EnumSet; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.luke.FieldFlag; +import org.apache.solr.index.NoMergePolicyFactory; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.schema.CustomAnalyzerStrField; import org.apache.solr.schema.IndexSchema; @@ -294,4 +295,59 @@ public void testCatchAllCopyField() throws Exception { deleteCore(); initCore("solrconfig.xml", "schema12.xml"); } + + /** + * SOLR-18125: Tests that Luke reports index flags for fields even when a document has been + * deleted. Uses NoMergePolicy so the deleted doc remains in the same segment, keeping liveDocs + * non-null and exposing the inverted liveDocs check in getFirstLiveDoc. + */ + @Test + public void testIndexFlagsWithDeletedDocs() throws Exception { + deleteCore(); + systemSetPropertySolrTestsMergePolicyFactory(NoMergePolicyFactory.class.getName()); + try { + initCore("solrconfig.xml", "schema12.xml"); + + // Three docs in a single commit so they share one segment. Delete the middle term + // ("bbb") and keep the lexical edges ("aaa" and "ccc") live, so the first term + // encountered is always a live doc regardless of ascending or descending iteration. + // The bug's inverted liveDocs check skips live docs, so getFirstLiveDoc returns null + // and index flags go missing. + assertU(adoc("id", "1", "solr_s", "aaa")); + assertU(adoc("id", "2", "solr_s", "bbb")); + assertU(adoc("id", "3", "solr_s", "ccc")); + assertU(commit()); + + assertU(delI("2")); + assertU(commit()); + + assertQ( + "index flags should be present for solr_s despite deletion in segment", + req("qt", "/admin/luke", "fl", "solr_s"), + getFieldXPathPrefix("solr_s") + "[@name='index']"); + + // Now test the inverse: delete the edges and keep the middle. The first term + // encountered ("aaa" or "ccc") is deleted, so getFirstLiveDoc must skip it and + // continue to the live term ("bbb"). This exercises the retry loop — without it, + // the method gives up after the first deleted term. + clearIndex(); + assertU(adoc("id", "4", "solr_s", "aaa")); + assertU(adoc("id", "5", "solr_s", "bbb")); + assertU(adoc("id", "6", "solr_s", "ccc")); + assertU(commit()); + + assertU(delI("4")); + assertU(delI("6")); + assertU(commit()); + + assertQ( + "index flags should be present for solr_s when edges are deleted", + req("qt", "/admin/luke", "fl", "solr_s"), + getFieldXPathPrefix("solr_s") + "[@name='index']"); + } finally { + deleteCore(); + System.clearProperty(SYSTEM_PROPERTY_SOLR_TESTS_MERGEPOLICYFACTORY); + initCore("solrconfig.xml", "schema12.xml"); + } + } }