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();