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 3509f70b12 OAK-11504 Elasticsearch: support flattened fields (#2099)
3509f70b12 is described below

commit 3509f70b12b290f8d90c44e554a30b1cc3010a99
Author: Thomas Mueller <[email protected]>
AuthorDate: Mon Mar 3 11:40:49 2025 +0100

    OAK-11504 Elasticsearch: support flattened fields (#2099)
    
    * OAK-11504 Elasticsearch: support flattened fields
    
    * OAK-11504 Elasticsearch: support flattened fields
    
    * OAK-11504 Elasticsearch: support flattened fields
    
    * OAK-11504 Elasticsearch: support flattened fields
    
    * Update 
oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticRegexPropertyIndexTest.java
    
    Co-authored-by: Fabrizio Fortino <[email protected]>
    
    * OAK-11504 Elasticsearch: support flattened fields
    
    * OAK-11504 Elasticsearch: support flattened fields
    
    * OAK-11504 Elasticsearch: support flattened fields
    
    * OAK-11504 Elasticsearch: support flattened fields
    
    * OAK-11504 Elasticsearch: support flattened fields - tests, default
    
    * OAK-11504 Elasticsearch: support flattened fields - test ordering
    
    * OAK-11504 Elasticsearch: support flattened fields - test ordering
    
    * OAK-11504 Elasticsearch: support flattened fields - test ordering
    
    ---------
    
    Co-authored-by: Fabrizio Fortino <[email protected]>
---
 .../index/elastic/ElasticIndexDefinition.java      |  23 ++++
 .../index/elastic/ElasticPropertyDefinition.java   |  32 ++++++
 .../index/elastic/index/ElasticDocument.java       |  16 +++
 .../index/elastic/index/ElasticDocumentMaker.java  |  14 ++-
 .../index/elastic/index/ElasticIndexHelper.java    |  12 ++
 .../index/elastic/query/ElasticRequestHandler.java |  27 ++---
 .../index/elastic/ElasticPropertyIndexTest.java    |   7 ++
 .../elastic/ElasticRegexPropertyIndexTest.java     | 121 +++++++++++++++++++++
 .../oak/plugins/index/search/FieldNames.java       |   5 +
 .../oak/plugins/index/search/IndexDefinition.java  |   6 +
 10 files changed, 243 insertions(+), 20 deletions(-)

diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDefinition.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDefinition.java
index 53e1f3c034..9d69551d1c 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDefinition.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDefinition.java
@@ -31,6 +31,7 @@ import java.util.stream.Stream;
 
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.collections.StreamUtils;
+import org.apache.jackrabbit.oak.plugins.index.search.FieldNames;
 import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
 import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
 import org.apache.jackrabbit.oak.plugins.index.search.PropertyDefinition;
@@ -320,6 +321,12 @@ public class ElasticIndexDefinition extends 
IndexDefinition {
         if (propertyDefinitions == null) {
             // if there are no property definitions we return the default 
keyword name
             // this can happen for properties that were not explicitly defined 
(eg: created with a regex)
+            ElasticPropertyDefinition pd = 
getMatchingRegexPropertyDefinition(propertyName);
+            if (pd != null) {
+                if (pd.isFlattened()) {
+                    return FieldNames.FLATTENED_FIELD_PREFIX + pd.nodeName + 
"." + propertyName;
+                }
+            }
             return propertyName + ".keyword";
         }
 
@@ -332,6 +339,22 @@ public class ElasticIndexDefinition extends 
IndexDefinition {
         return field;
     }
 
+    /**
+     * Try to get the matching regular expression property definition, if any
+     *
+     * @param propertyName the property name (may not be null)
+     * @return the property definition, or null if not found
+     */
+    private ElasticPropertyDefinition 
getMatchingRegexPropertyDefinition(String propertyName) {
+        for (IndexingRule rule : getDefinedRules()) {
+            PropertyDefinition pd = rule.getConfig(propertyName);
+            if (pd != null && pd.isRegexp) {
+                return (ElasticPropertyDefinition) pd;
+            }
+        }
+        return null;
+    }
+
     public boolean isAnalyzed(List<PropertyDefinition> propertyDefinitions) {
         return propertyDefinitions.stream().anyMatch(pd -> pd.analyzed);
     }
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyDefinition.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyDefinition.java
index 0a4f275cdd..d73ccb27c9 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyDefinition.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyDefinition.java
@@ -43,6 +43,17 @@ public class ElasticPropertyDefinition extends 
PropertyDefinition {
     private static final String PROP_USE_IN_FULL_TEXT_QUERY = 
"useInFullTextQuery";
     private final boolean useInFullTextQuery;
 
+    /**
+     * Whether regex properties are flattened (using the "flattened" field 
type)
+     */
+    public static final String PROP_IS_FLATTENED = "isFlattened";
+    private final boolean isFlattened;
+
+    /**
+     * The default value for the "isFlattened" property.
+     */
+    public static final boolean PROP_IS_FLATTENED_DEFAULT = false;
+
     public ElasticPropertyDefinition(IndexDefinition.IndexingRule idxDefn, 
String nodeName, NodeState defn) {
         super(idxDefn, nodeName, defn);
         if (this.useInSimilarity) {
@@ -53,6 +64,14 @@ public class ElasticPropertyDefinition extends 
PropertyDefinition {
                     getOptionalValue(defn, PROP_CANDIDATES, 
DEFAULT_CANDIDATES));
         }
         this.useInFullTextQuery = this.dynamicBoost && getOptionalValue(defn, 
PROP_USE_IN_FULL_TEXT_QUERY, true);
+        boolean flattened = getOptionalValue(defn, PROP_IS_FLATTENED, 
PROP_IS_FLATTENED_DEFAULT);
+        if (analyzed) {
+            // if analyzed is enabled, then flattened needs to be disabled,
+            // because flattened types do not support fulltext queries
+            // in the same way
+            flattened = false;
+        }
+        this.isFlattened = flattened;
     }
 
     @Override
@@ -75,7 +94,19 @@ public class ElasticPropertyDefinition extends 
PropertyDefinition {
     public boolean useInFullTextQuery() {
         return useInFullTextQuery;
     }
+  
+    public boolean isFlattened() {
+        return isFlattened;
+    }
 
+    @Override
+    public String toString() {
+        return "ElasticPropertyDefinition{" + super.toString() +
+                ", useInFullTextQuery=" + useInFullTextQuery +
+                ", isFlattened=" + isFlattened +
+                '}';
+    }
+      
     /**
      * Class for defining parameters of approximate knn search on dense_vector 
fields
      * <a 
href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html";>...</a>
 and
@@ -134,5 +165,6 @@ public class ElasticPropertyDefinition extends 
PropertyDefinition {
         public int getCandidates() {
             return candidates;
         }
+      
     }
 }
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocument.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocument.java
index 1d038835a5..3c7dc6f4f3 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocument.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocument.java
@@ -195,4 +195,20 @@ public class ElasticDocument {
         return propertiesToRemove;
     }
 
+    @Override
+    public String toString() {
+        StringBuilder buff = new StringBuilder();
+        buff.append("path:").append(path).append('\n');
+        if (!fulltext.isEmpty()) {
+            buff.append("fulltext:").append(fulltext).append('\n');
+        }
+        if (!properties.isEmpty()) {
+            buff.append("properties:").append(properties).append('\n');
+        }
+        if (!dynamicProperties.isEmpty()) {
+            
buff.append("dynamicProperties:").append(dynamicProperties).append('\n');
+        }
+        return buff.toString();
+    }
+
 }
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocumentMaker.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocumentMaker.java
index 9006fa3861..5316cf1127 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocumentMaker.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticDocumentMaker.java
@@ -23,6 +23,7 @@ import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.log.LogSilencer;
 import org.apache.jackrabbit.oak.plugins.index.elastic.ElasticIndexDefinition;
+import 
org.apache.jackrabbit.oak.plugins.index.elastic.ElasticPropertyDefinition;
 import org.apache.jackrabbit.oak.plugins.index.search.Aggregate;
 import org.apache.jackrabbit.oak.plugins.index.search.FieldNames;
 import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
@@ -169,6 +170,13 @@ public class ElasticDocumentMaker extends 
FulltextDocumentMaker<ElasticDocument>
         // If the actual property value is different from the property type 
defined in the index definition/mapping - this will try to convert the property 
if possible,
         // otherwise will log a warning and not try and add the property to 
index. If we try and index incompatible data types (like String to Date),
         // we would get an exception while indexing the node on elastic search 
and other properties for the node will also don't get indexed. (See OAK-9665).
+        String fieldName = pname;
+        if (pd.isRegexp) {
+            ElasticPropertyDefinition epd = (ElasticPropertyDefinition) pd;
+            if (epd.isFlattened()) {
+                fieldName = FieldNames.FLATTENED_FIELD_PREFIX + epd.nodeName + 
"." + pname;
+            }
+        }
         int tag = pd.getType();
         Object f;
         try {
@@ -184,12 +192,12 @@ public class ElasticDocumentMaker extends 
FulltextDocumentMaker<ElasticDocument>
                 f = property.getValue(Type.STRING, i);
             }
 
-            doc.addProperty(pname, f);
+            doc.addProperty(fieldName, f);
         } catch (Exception e) {
             if (!LOG_SILENCER.silence(LOG_KEY_COULD_NOT_CONVERT_PROPERTY)) {
                 LOG.warn(
-                        "[{}] Ignoring property. Could not convert property {} 
of type {} to type {} for path {}. Error: {}",
-                        getIndexName(), pname,
+                        "[{}] Ignoring property. Could not convert property {} 
(field {}) of type {} to type {} for path {}. Error: {}",
+                        getIndexName(), pname, fieldName,
                         Type.fromTag(property.getType().tag(), false),
                         Type.fromTag(tag, false), path, e.toString());
             }
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexHelper.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexHelper.java
index b49022403b..7ec836d132 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexHelper.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexHelper.java
@@ -23,6 +23,7 @@ import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.elastic.ElasticIndexDefinition;
 import 
org.apache.jackrabbit.oak.plugins.index.elastic.ElasticPropertyDefinition;
 import org.apache.jackrabbit.oak.plugins.index.search.FieldNames;
+import 
org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition.IndexingRule;
 import org.apache.jackrabbit.oak.plugins.index.search.PropertyDefinition;
 import org.jetbrains.annotations.NotNull;
 
@@ -235,6 +236,17 @@ class ElasticIndexHelper {
     private static void mapIndexRules(@NotNull TypeMapping.Builder builder,
                                       @NotNull ElasticIndexDefinition 
indexDefinition) {
         checkIndexRules(indexDefinition);
+        for (IndexingRule rule : indexDefinition.getDefinedRules()) {
+            Iterable<PropertyDefinition> iterable = 
rule.getNamePatternsProperties()::iterator;
+            for (PropertyDefinition pd : iterable) {
+                ElasticPropertyDefinition epd = (ElasticPropertyDefinition) pd;
+                if (epd.isFlattened()) {
+                    Property.Builder pBuilder = new Property.Builder();
+                    pBuilder.flattened(b2 -> b2.index(true));
+                    builder.properties(FieldNames.FLATTENED_FIELD_PREFIX + 
pd.nodeName, pBuilder.build());
+                }
+            }
+        }
         for (Map.Entry<String, List<PropertyDefinition>> entry : 
indexDefinition.getPropertiesByName().entrySet()) {
             final String name = entry.getKey();
             final List<PropertyDefinition> propertyDefinitions = 
entry.getValue();
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticRequestHandler.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticRequestHandler.java
index 8b11566216..330aecc167 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticRequestHandler.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/query/ElasticRequestHandler.java
@@ -565,7 +565,7 @@ public class ElasticRequestHandler {
                     if (elasticIndexDefinition.inferenceDefinition != null && 
elasticIndexDefinition.inferenceDefinition.queries != null) {
                         bqBuilder.must(m -> m.bool(b -> inference(b, 
propertyName, text, pr, includeDynamicBoostedValues)));
                     } else {
-                        QueryStringQuery.Builder qsqBuilder = 
fullTextQuery(text, getElasticFieldName(propertyName), pr, 
includeDynamicBoostedValues);
+                        QueryStringQuery.Builder qsqBuilder = 
fullTextQuery(text, getElasticFulltextFieldName(propertyName), pr, 
includeDynamicBoostedValues);
                         bqBuilder.must(m -> m.queryString(qsqBuilder.build()));
                     }
                 }
@@ -604,7 +604,7 @@ public class ElasticRequestHandler {
             }
         }
 
-        QueryStringQuery.Builder qsqBuilder = fullTextQuery(queryText, 
getElasticFieldName(propertyName), pr, dbEnabled);
+        QueryStringQuery.Builder qsqBuilder = fullTextQuery(queryText, 
getElasticFulltextFieldName(propertyName), pr, dbEnabled);
 
         // the query can be null if no inference query is eligible for the 
given text or the min terms are not met
         // in this case, we fall back to the default full-text query
@@ -931,33 +931,26 @@ public class ElasticRequestHandler {
             }
             default: {
                 if (pr.isLike) {
-                    return like(propertyName, pr.first.getValue(Type.STRING));
+                    in = like(propertyName, pr.first.getValue(Type.STRING));
+                } else {
+                    // TODO Confirm that all other types can be treated as 
string
+                    in = newPropertyRestrictionQuery(field, pr, value -> 
value.getValue(Type.STRING));
                 }
-
-                // TODO Confirm that all other types can be treated as string
-                in = newPropertyRestrictionQuery(field, pr, value -> 
value.getValue(Type.STRING));
             }
         }
-
         if (in != null) {
             return in;
         }
-
         throw new IllegalStateException("PropertyRestriction not handled " + 
pr + " for index " + defn);
     }
 
-    private String getElasticFieldName(@Nullable String p) {
-        if (p == null) {
+    private String getElasticFulltextFieldName(@Nullable String propertyName) {
+        if (propertyName == null || "*".equals(propertyName)) {
             return FieldNames.FULLTEXT;
         }
-
         if (planResult.isPathTransformed()) {
-            p = PathUtils.getName(p);
-        }
-
-        if ("*".equals(p)) {
-            p = FieldNames.FULLTEXT;
+            propertyName = PathUtils.getName(propertyName);
         }
-        return p;
+        return propertyName;
     }
 }
diff --git 
a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
 
b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
index f2055fe90e..0e930f5c47 100644
--- 
a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
+++ 
b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
@@ -265,6 +265,13 @@ public class ElasticPropertyIndexTest extends 
ElasticAbstractQueryTest {
 
     @Test
     public void indexFailuresWithFailOnErrorOn() throws Exception {
+        if (ElasticPropertyDefinition.PROP_IS_FLATTENED_DEFAULT) {
+            // if "flattened" enabled by default,
+            // then the test doesn't make sense.
+            // alternatively, disable "flattened" in the index definition;
+            // but this is already tested in ElasticRegexPropertyIndexTest
+            return;
+        }
         IndexDefinitionBuilder builder = createIndex("a");
         builder.includedPaths("/test")
                 .indexRule("nt:base")
diff --git 
a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticRegexPropertyIndexTest.java
 
b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticRegexPropertyIndexTest.java
new file mode 100644
index 0000000000..1804a97735
--- /dev/null
+++ 
b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticRegexPropertyIndexTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.elastic;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
+import 
org.apache.jackrabbit.oak.plugins.index.search.util.IndexDefinitionBuilder;
+import 
org.apache.jackrabbit.oak.plugins.index.search.util.IndexDefinitionBuilder.PropertyRule;
+import org.junit.Test;
+
+public class ElasticRegexPropertyIndexTest extends ElasticAbstractQueryTest {
+
+    @Test
+    public void regexPropertyWithFlattened() throws Exception {
+        IndexDefinitionBuilder builder = createIndex("allProperties");
+        PropertyRule prop = 
builder.indexRule("nt:base").property("allProperties");
+        
prop.getBuilderTree().setProperty(FulltextIndexConstants.PROP_IS_REGEX, true);
+        prop.getBuilderTree().setProperty(FulltextIndexConstants.PROP_NAME, 
"^[^\\/]*$");
+        prop.nodeScopeIndex();
+        
prop.getBuilderTree().setProperty(ElasticPropertyDefinition.PROP_IS_FLATTENED, 
true);
+        
+        setIndex("test1", builder);
+        root.commit();
+
+        Tree test = root.getTree("/").addChild("test");
+        test.addChild("a").setProperty("propa", "foo");
+        test.addChild("b").setProperty("propa", "foo");
+        test.addChild("c").setProperty("propa", "foo2");
+        test.addChild("d").setProperty("propc", "foo");
+        test.addChild("e").setProperty("propd", "foo2");
+        test.addChild("f").setProperty("propd", "foo1");
+
+        // create 10k nodes with different property names to have high 
cardinality;
+        // without flattened fields, this will break the test with
+        // "Limit of total fields [1000] has been exceeded"
+        for (int i = 0; i < 10_000; i++) {
+            test.addChild("node" + i).setProperty("prop" + i, "foo");
+        }
+        root.commit();
+
+        String propaQuery = "select [jcr:path] from [nt:base] where [propa] = 
'foo'";
+
+        assertEventually(() -> {
+            String explain = explain(propaQuery);
+            assertThat(explain, containsString("elasticsearch:test1"));
+            assertThat(explain, 
containsString("[{\"term\":{\"flat:allProperties.propa\":{\"value\":\"foo\"}}}]"));
+            assertQuery(propaQuery, List.of("/test/a", "/test/b"));
+        });
+
+        String propaOrderQuery = "select [jcr:path] from [nt:base] where 
[propd] like 'foo%' order by [propd]";
+
+        assertEventually(() -> {
+            String explain = explain(propaOrderQuery);
+            assertThat(explain, containsString("elasticsearch:test1"));
+            assertThat(explain, 
containsString("\"query\":{\"bool\":{\"filter\":[{\"prefix\":{\"flat:allProperties.propd\":{\"value\":\"foo\"}}}]}}"));
+            assertThat(explain, 
containsString("\"sort\":[{\"flat:allProperties.propd\":{\"order\":\"asc\"}},{\":path\":{\"order\":\"asc\"}}]"));
+            assertThat(explain, containsString("sortOrder: [{ propertyName : 
propd, propertyType : UNDEFINED, order : ASCENDING }]"));
+            assertQuery(propaOrderQuery, List.of("/test/f", "/test/e"));
+        });
+
+    }
+
+    @Test
+    public void regexPropertyWithoutFlattened() throws Exception {
+        IndexDefinitionBuilder builder = createIndex("allProperties");
+        PropertyRule prop = 
builder.indexRule("nt:base").property("allProperties");
+        
prop.getBuilderTree().setProperty(FulltextIndexConstants.PROP_IS_REGEX, true);
+        prop.getBuilderTree().setProperty(FulltextIndexConstants.PROP_NAME, 
"^[^\\/]*$");
+        prop.nodeScopeIndex();
+        
prop.getBuilderTree().setProperty(ElasticPropertyDefinition.PROP_IS_FLATTENED, 
false);
+
+        setIndex("test1", builder);
+        root.commit();
+
+        Tree test = root.getTree("/").addChild("test");
+        test.addChild("a").setProperty("propa", "foo");
+        test.addChild("b").setProperty("propa", "foo");
+        test.addChild("c").setProperty("propa", "foo2");
+        test.addChild("d").setProperty("propc", "foo");
+        test.addChild("e").setProperty("propd", "foo");
+
+        // create 10k nodes with different property names to have high 
cardinality;
+        // without flattened fields, this will break the test with
+        // "Limit of total fields [1000] has been exceeded"
+        for (int i = 0; i < 10_000; i++) {
+            test.addChild("node" + i).setProperty("prop" + i, "foo");
+        }
+        try {
+            root.commit();
+            fail();
+        } catch (CommitFailedException e) {
+            String msg = e.getMessage();
+            assertTrue(msg, msg.contains("Failed to index the node"));
+            // Typically, the root cause is "Limit of total fields [1000] has 
been exceeded"
+            // but something this is suppressed, and so we can not have an 
assertion on it
+        }
+    }
+
+}
diff --git 
a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/FieldNames.java
 
b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/FieldNames.java
index a8d2237702..a86822ba36 100644
--- 
a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/FieldNames.java
+++ 
b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/FieldNames.java
@@ -84,6 +84,11 @@ public final class FieldNames {
      */
     public static final String ANALYZED_FIELD_PREFIX = "full:";
 
+    /**
+     * Prefix for all field names that are flattened.
+     */
+    public static final String FLATTENED_FIELD_PREFIX = "flat:";
+
     /**
      * Prefix used for storing fulltext of relative node
      */
diff --git 
a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java
 
b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java
index 9ca458b323..7a7d1bd4bf 100644
--- 
a/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java
+++ 
b/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/IndexDefinition.java
@@ -1271,6 +1271,12 @@ public class IndexDefinition implements 
Aggregate.AggregateMapper {
                 return config;
             } else if (!namePatterns.isEmpty()) {
                 // check patterns
+                if (NodeStateUtils.isHidden(propertyName)) {
+                    // hidden properties (eg. ":nodeName") do match the regex,
+                    // and we should probably ignore them;
+                    // but doing so would break "bug compatibility"
+                    // return null;
+                }
                 for (NamePattern np : namePatterns) {
                     if (np.matches(propertyName)) {
                         return np.getConfig();

Reply via email to