This is an automated email from the ASF dual-hosted git repository.

thomasm pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 763031951c OAK-10490 Suggest queries return duplicate entries if 
prefetch is enabled (#1148)
763031951c is described below

commit 763031951c3962aae875ec315d961ab14798dc56
Author: Thomas Mueller <[email protected]>
AuthorDate: Fri Oct 13 13:44:01 2023 +0200

    OAK-10490 Suggest queries return duplicate entries if prefetch is enabled 
(#1148)
    
    * OAK-10490 Suggest queries return duplicate entries if prefetch is enabled
    
    * OAK-10490 Suggest queries return duplicate entries if prefetch is enabled
    
    * OAK-10490 Suggest queries return duplicate entries if prefetch is enabled
    
    * OAK-10490 Suggest queries return duplicate entries if prefetch is enabled
---
 .../jackrabbit/oak/query/QueryEngineSettings.java  |   2 +-
 .../index/search/spi/query/FulltextIndex.java      | 152 +++++++++++--------
 .../search/spi/query/FulltextIndexPlanner.java     |   2 +-
 .../index/search/spi/query/FulltextIndexTest.java  | 161 ++++++++++++++++++++-
 4 files changed, 248 insertions(+), 69 deletions(-)

diff --git 
a/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java
 
b/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java
index 0800f5520b..0bf4a9a48d 100644
--- 
a/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java
+++ 
b/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineSettings.java
@@ -61,7 +61,7 @@ public class QueryEngineSettings implements 
QueryEngineSettingsMBean, QueryLimit
 
     public static final String OAK_QUERY_PREFETCH_COUNT = "oak.prefetchCount";
 
-    public static final String FT_NAME_PREFETCH_FOR_QUERIES = "FT_OAK-9893";
+    public static final String FT_NAME_PREFETCH_FOR_QUERIES = "FT_OAK-10490";
 
     public static final int DEFAULT_PREFETCH_COUNT = 
Integer.getInteger(OAK_QUERY_PREFETCH_COUNT, -1);
 
diff --git 
a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java
 
b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java
index c0807ad676..6d1b2663bb 100644
--- 
a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java
+++ 
b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndex.java
@@ -400,11 +400,16 @@ public abstract class FulltextIndex implements 
AdvancedQueryIndex, QueryIndex, N
 
         private final Cursor pathCursor;
         private final String pathPrefix;
-        FulltextResultRow currentRow;
         private final SizeEstimator sizeEstimator;
-        private long estimatedSize;
         private final int numberOfFacets;
 
+        // the cached value
+        private long estimatedSize;
+
+        // the current row in the pathCursor
+        // (so we don't have to extend the PathCursor)
+        FulltextResultRow currentRowInPathIterator;
+
         public FulltextPathCursor(final Iterator<FulltextResultRow> it, final 
IteratorRewoundStateProvider iterStateProvider,
                                   final IndexPlan plan, QueryLimits settings, 
SizeEstimator sizeEstimator) {
             pathPrefix = plan.getPathPrefix();
@@ -425,7 +430,7 @@ public abstract class FulltextIndex implements 
AdvancedQueryIndex, QueryIndex, N
                         readCount = 0;
                         rewoundCount = iterStateProvider.rewoundCount();
                     }
-                    currentRow = it.next();
+                    currentRowInPathIterator = it.next();
                     readCount++;
                     if (readCount % TRAVERSING_WARNING == 0) {
                         Cursors.checkReadLimit(readCount, settings);
@@ -436,7 +441,7 @@ public abstract class FulltextIndex implements 
AdvancedQueryIndex, QueryIndex, N
                             log.warn("Index-Traversed {} nodes with filter 
{}", readCount, plan.getFilter());
                         }
                     }
-                    return currentRow.path;
+                    return currentRowInPathIterator.path;
                 }
 
                 @Override
@@ -464,68 +469,13 @@ public abstract class FulltextIndex implements 
AdvancedQueryIndex, QueryIndex, N
 
         @Override
         public IndexRow next() {
-            final IndexRow pathRow = pathCursor.next();
-            return new IndexRow() {
-
-                @Override
-                public boolean isVirtualRow() {
-                    return currentRow.isVirutal;
-                }
-
-                @Override
-                public String getPath() {
-                    String sub = pathRow.getPath();
-                    if (isVirtualRow()) {
-                        return sub;
-                    } else if (!"".equals(pathPrefix) && 
PathUtils.denotesRoot(sub)) {
-                        return pathPrefix;
-                    } else if (PathUtils.isAbsolute(sub)) {
-                        return pathPrefix + sub;
-                    } else {
-                        return PathUtils.concat(pathPrefix, sub);
-                    }
-                }
+            IndexRow pathRow = pathCursor.next();
 
-                @Override
-                public PropertyValue getValue(String columnName) {
-                    // overlay the score
-                    if (QueryConstants.JCR_SCORE.equals(columnName)) {
-                        return PropertyValues.newDouble(currentRow.score);
-                    }
-                    if (QueryConstants.REP_SPELLCHECK.equals(columnName) || 
QueryConstants.REP_SUGGEST.equals(columnName)) {
-                        return PropertyValues.newString(currentRow.suggestion);
-                    }
-                    if 
(QueryConstants.OAK_SCORE_EXPLANATION.equals(columnName)) {
-                        return 
PropertyValues.newString(currentRow.explanation);
-                    }
-                    if (columnName.startsWith(QueryConstants.REP_EXCERPT)) {
-                        String excerpt = currentRow.excerpts.get(columnName);
-                        if (excerpt != null) {
-                            return PropertyValues.newString(excerpt);
-                        }
-                    }
-                    if (columnName.startsWith(QueryConstants.REP_FACET)) {
-                        try {
-                            List<Facet> facets = 
currentRow.getFacets(numberOfFacets, columnName);
-                            if (facets != null) {
-                                JsopWriter writer = new JsopBuilder();
-                                writer.object();
-                                for (Facet f : facets) {
-                                    
writer.key(f.getLabel()).value(f.getCount());
-                                }
-                                writer.endObject();
-                                return 
PropertyValues.newString(writer.toString());
-                            }
-                        } catch (IOException | RuntimeException e) {
-                            LOG.warn(e.getMessage());
-                            LOG.debug(e.getMessage(), e);
-                            throw new RuntimeException(e);
-                        }
-                    }
-                    return pathRow.getValue(columnName);
-                }
+            // currentRowInPathIterator is changed as a side effect
+            // of calling pathCursor.next()
+            FulltextResultRow row = currentRowInPathIterator;
 
-            };
+            return new IndexResultRow(pathRow, row, pathPrefix, 
numberOfFacets);
         }
 
         @Override
@@ -537,6 +487,80 @@ public abstract class FulltextIndex implements 
AdvancedQueryIndex, QueryIndex, N
         }
     }
 
+    private static class IndexResultRow implements IndexRow {
+
+        private final IndexRow pathRow;
+        private final FulltextResultRow resultRow;
+        private final String pathPrefix;
+        private final int numberOfFacets;
+
+        IndexResultRow(IndexRow pathRow, FulltextResultRow row, String 
pathPrefix, int numberOfFacets) {
+            this.pathRow = pathRow;
+            this.resultRow = row;
+            this.pathPrefix = pathPrefix;
+            this.numberOfFacets = numberOfFacets;
+        }
+
+        @Override
+        public boolean isVirtualRow() {
+            return resultRow.isVirutal;
+        }
+
+        @Override
+        public String getPath() {
+            String sub = pathRow.getPath();
+            if (isVirtualRow()) {
+                return sub;
+            } else if (!"".equals(pathPrefix) && PathUtils.denotesRoot(sub)) {
+                return pathPrefix;
+            } else if (PathUtils.isAbsolute(sub)) {
+                return pathPrefix + sub;
+            } else {
+                return PathUtils.concat(pathPrefix, sub);
+            }
+        }
+
+        @Override
+        public PropertyValue getValue(String columnName) {
+            // overlay the score
+            if (QueryConstants.JCR_SCORE.equals(columnName)) {
+                return PropertyValues.newDouble(resultRow.score);
+            }
+            if (QueryConstants.REP_SPELLCHECK.equals(columnName) || 
QueryConstants.REP_SUGGEST.equals(columnName)) {
+                return PropertyValues.newString(resultRow.suggestion);
+            }
+            if (QueryConstants.OAK_SCORE_EXPLANATION.equals(columnName)) {
+                return PropertyValues.newString(resultRow.explanation);
+            }
+            if (columnName.startsWith(QueryConstants.REP_EXCERPT)) {
+                String excerpt = resultRow.excerpts.get(columnName);
+                if (excerpt != null) {
+                    return PropertyValues.newString(excerpt);
+                }
+            }
+            if (columnName.startsWith(QueryConstants.REP_FACET)) {
+                try {
+                    List<Facet> facets = resultRow.getFacets(numberOfFacets, 
columnName);
+                    if (facets != null) {
+                        JsopWriter writer = new JsopBuilder();
+                        writer.object();
+                        for (Facet f : facets) {
+                            writer.key(f.getLabel()).value(f.getCount());
+                        }
+                        writer.endObject();
+                        return PropertyValues.newString(writer.toString());
+                    }
+                } catch (IOException | RuntimeException e) {
+                    LOG.warn(e.getMessage());
+                    LOG.debug(e.getMessage(), e);
+                    throw new RuntimeException(e);
+                }
+            }
+            return pathRow.getValue(columnName);
+        }
+
+    };
+
     public interface IteratorRewoundStateProvider {
         int rewoundCount();
     }
diff --git 
a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java
 
b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java
index 8ca45cb0b7..66d4130c5f 100644
--- 
a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java
+++ 
b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexPlanner.java
@@ -1163,7 +1163,7 @@ public class FulltextIndexPlanner {
             nodeNameRestriction = true;
         }
 
-        private void disableUniquePaths(){
+        public void disableUniquePaths(){
             uniquePathsRequired = false;
         }
     }
diff --git 
a/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexTest.java
 
b/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexTest.java
index 9bb4f8b159..316081b597 100644
--- 
a/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexTest.java
+++ 
b/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/FulltextIndexTest.java
@@ -18,11 +18,32 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.search.spi.query;
 
-import org.junit.Test;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.search.SizeEstimator;
+import 
org.apache.jackrabbit.oak.plugins.index.search.spi.query.FulltextIndex.FulltextPathCursor;
+import 
org.apache.jackrabbit.oak.plugins.index.search.spi.query.FulltextIndex.FulltextResultRow;
+import 
org.apache.jackrabbit.oak.plugins.index.search.spi.query.FulltextIndex.IteratorRewoundStateProvider;
+import 
org.apache.jackrabbit.oak.plugins.index.search.spi.query.FulltextIndexPlanner.PlanResult;
+import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
+import org.apache.jackrabbit.oak.spi.query.IndexRow;
+import org.apache.jackrabbit.oak.spi.query.QueryConstants;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex.IndexPlan;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex.OrderEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
 public class FulltextIndexTest {
 
     @Test
@@ -36,7 +57,141 @@ public class FulltextIndexTest {
         field = FulltextIndex.parseFacetField("rep:facet(jcr:primaryType)");
         assertNotNull(field);
         assertEquals("jcr:primaryType", field);
+    }
+
+    /**
+     * Test that we can read the rows first, and then read the data from the 
rows.
+     */
+    @Test
+    public void fulltextPathCursorTest() {
+        ArrayList<FulltextResultRow> rowList = new ArrayList<>();
+        String s1 = "s1", s2 = "s2";
+        rowList.add(new FulltextResultRow(s1, 1));
+        rowList.add(new FulltextResultRow(s2, 2));
+        FulltextPathCursor fulltextPathCursor = new 
TestFulltextPathCursor(rowList.iterator());
+        IndexRow row1 = fulltextPathCursor.next();
+        IndexRow row2 = fulltextPathCursor.next();
+        String suggest1 = 
row1.getValue(QueryConstants.REP_SUGGEST).getValue(Type.STRING);
+        String suggest2 = 
row2.getValue(QueryConstants.REP_SUGGEST).getValue(Type.STRING);
+        assertEquals(s1, suggest1);
+        assertEquals(s2, suggest2);
 
     }
-}
 
+    static class TestFulltextPathCursor extends FulltextPathCursor {
+        public TestFulltextPathCursor(Iterator<FulltextResultRow> it) {
+            super(it, new IteratorRewoundStateProvider() {
+
+                @Override
+                public int rewoundCount() {
+                    return 0;
+                }
+
+            }, new TestIndexPlan(), new QueryEngineSettings(), new 
SizeEstimator() {
+
+                @Override
+                public long getSize() {
+                    return 0;
+                }
+
+            });
+        }
+
+    }
+
+    static class TestIndexPlan implements IndexPlan {
+
+        @Override
+        public double getCostPerExecution() {
+            return 0;
+        }
+
+        @Override
+        public double getCostPerEntry() {
+            return 0;
+        }
+
+        @Override
+        public long getEstimatedEntryCount() {
+            return 0;
+        }
+
+        @Override
+        public Filter getFilter() {
+            return null;
+        }
+
+        @Override
+        public void setFilter(Filter filter) {
+        }
+
+        @Override
+        public boolean isDelayed() {
+            return false;
+        }
+
+        @Override
+        public boolean isFulltextIndex() {
+            return false;
+        }
+
+        @Override
+        public boolean includesNodeData() {
+            return false;
+        }
+
+        @Override
+        public List<OrderEntry> getSortOrder() {
+            return null;
+        }
+
+        @Override
+        public NodeState getDefinition() {
+            return null;
+        }
+
+        @Override
+        public String getPathPrefix() {
+            return null;
+        }
+
+        @Override
+        public boolean getSupportsPathRestriction() {
+            return false;
+        }
+
+        @Override
+        public @Nullable PropertyRestriction getPropertyRestriction() {
+            return null;
+        }
+
+        @Override
+        public IndexPlan copy() {
+            return null;
+        }
+
+        @Override
+        public @Nullable Object getAttribute(String name) {
+            if (name.equals(FulltextIndex.ATTR_PLAN_RESULT)) {
+                IndexDefinition indexDef = new 
IndexDefinition(EmptyNodeState.EMPTY_NODE, EmptyNodeState.EMPTY_NODE,
+                        "");
+                PlanResult pr = new PlanResult("test", indexDef, null);
+                pr.disableUniquePaths();
+                return pr;
+            }
+            return null;
+        }
+
+        @Override
+        public @Nullable String getPlanName() {
+            return null;
+        }
+
+        @Override
+        public boolean isDeprecated() {
+            return false;
+        }
+
+    }
+
+}

Reply via email to