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

thomasmueller 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 aeaf421c8e OAK-12262 Simplified index mgmt: support indexes without 
version number (#2961)
aeaf421c8e is described below

commit aeaf421c8ee0cee435e480f0e2f377e649cc8cc3
Author: Thomas Mueller <[email protected]>
AuthorDate: Fri Jun 19 16:18:52 2026 +0200

    OAK-12262 Simplified index mgmt: support indexes without version number 
(#2961)
---
 .../jackrabbit/oak/plugins/index/IndexName.java    |   7 +-
 .../oak/plugins/index/diff/DiffIndexMerger.java    |  13 ++-
 .../oak/plugins/index/IndexNameTest.java           |  10 ++
 .../oak/plugins/index/diff/DiffIndexTest.java      | 107 +++++++++++++++++++++
 .../oak/index/merge/IndexDefMergerUtils.java       |   4 +-
 .../index/merge/IndexDefMergerScenariosTest.java   |   4 +-
 .../oak/index/merge/unversioned-index.json         |  42 ++++++++
 7 files changed, 176 insertions(+), 11 deletions(-)

diff --git 
a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexName.java 
b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexName.java
index b453804ebf..f6153fadc7 100644
--- 
a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexName.java
+++ 
b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexName.java
@@ -186,7 +186,9 @@ public class IndexName implements Comparable<IndexName> {
         IndexName latest = null;
         for (IndexName n : all) {
             if (n.baseName.equals(baseName)) {
-                if (compareTo(n) > 0 && n.customerVersion == 0) {
+                // for unversioned indexes, allow equal (the index is its own 
ancestor)
+                boolean sameOrOlder = isVersioned ? compareTo(n) > 0 : 
compareTo(n) >= 0;
+                if (sameOrOlder && n.customerVersion == 0) {
                     if (latest == null || n.compareTo(latest) > 0) {
                         latest = n;
                     }
@@ -283,6 +285,9 @@ public class IndexName implements Comparable<IndexName> {
     }
 
     public String nextCustomizedName() {
+        if (productVersion == 0) {
+            return baseName + "-custom-" + (customerVersion + 1);
+        }
         return baseName + "-" + productVersion + "-custom-" + (customerVersion 
+ 1);
     }
 
diff --git 
a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diff/DiffIndexMerger.java
 
b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diff/DiffIndexMerger.java
index ca2e31f854..25346fc9e6 100644
--- 
a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diff/DiffIndexMerger.java
+++ 
b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/diff/DiffIndexMerger.java
@@ -340,10 +340,6 @@ public class DiffIndexMerger {
         String prefix = "/oak:index/";
         for (String key : combined.getChildren().keySet()) {
             IndexName name = IndexName.parse(key.substring(prefix.length()));
-            if (!name.isVersioned()) {
-                log("Ignoring unversioned index {}", name);
-                continue;
-            }
             if (!name.getBaseName().equals(indexName)) {
                 continue;
             }
@@ -457,9 +453,12 @@ public class DiffIndexMerger {
                         "-custom-" + (latestCustomized.getCustomerVersion() + 
1);
             } else {
                 // customized OOTB index: use the latest product as the base
-                key = prefix + indexName +
-                        "-" + latestProduct.getProductVersion() +
-                        "-custom-";
+                int productVersion = latestProduct.getProductVersion();
+                if (productVersion == 0) {
+                    key = prefix + indexName + "-custom-";
+                } else {
+                    key = prefix + indexName + "-" + productVersion + 
"-custom-";
+                }
                 if (latestCustomized != null) {
                     key += (latestCustomized.getCustomerVersion() + 1);
                 } else {
diff --git 
a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexNameTest.java
 
b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexNameTest.java
index 21715b20c3..cebbdd517c 100644
--- 
a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexNameTest.java
+++ 
b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexNameTest.java
@@ -116,6 +116,16 @@ public class IndexNameTest {
         }
     }
 
+    @Test
+    public void nextCustomizedName() {
+        // versioned index: product version is preserved
+        assertEquals("asset-1-custom-1", 
IndexName.parse("asset-1").nextCustomizedName());
+        assertEquals("asset-1-custom-2", 
IndexName.parse("asset-1-custom-1").nextCustomizedName());
+        // unversioned index: no "-0-" in the result
+        assertEquals("asset-custom-1", 
IndexName.parse("asset").nextCustomizedName());
+        assertEquals("asset-custom-2", 
IndexName.parse("asset-custom-1").nextCustomizedName());
+    }
+
     @Test
     public void filterNewestIndexes() {
 
diff --git 
a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/diff/DiffIndexTest.java
 
b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/diff/DiffIndexTest.java
index 50f9e2efd2..edefd828f1 100644
--- 
a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/diff/DiffIndexTest.java
+++ 
b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/diff/DiffIndexTest.java
@@ -22,6 +22,7 @@ import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_DISABL
 import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
@@ -259,6 +260,112 @@ public class DiffIndexTest {
         assertSameJson("{}", repositoryDefinitions.toString());
     }
 
+    @Test
+    public void testDiffIndexUpdateUnversionedOotbExplicit() throws Exception {
+        NodeStore store = new MemoryNodeStore(INITIAL_CONTENT);
+
+        List<IndexEditorProvider> indexEditors = List.of(
+                new ReferenceEditorProvider(), new 
PropertyIndexEditorProvider(), new NodeCounterEditorProvider());
+        IndexEditorProvider provider = 
CompositeIndexEditorProvider.compose(indexEditors);
+        EditorHook hook = new EditorHook(new IndexUpdateProvider(provider));
+
+        // seed the store with an unversioned OOTB index /oak:index/test (type 
lucene)
+        NodeBuilder builder = store.getRoot().builder();
+        builder.child(INDEX_DEFINITIONS_NAME).child("test")
+                .setProperty("jcr:primaryType", 
IndexConstants.INDEX_DEFINITIONS_NODE_TYPE, Type.NAME)
+                .setProperty(TYPE_PROPERTY_NAME, "lucene");
+        store.merge(builder, hook, CommitInfo.EMPTY);
+
+        // diff adds indexRules to the product index
+        storeDiff(store, "2026-01-01T00:00:00.000Z",
+                "{ \"test\": {"
+                + "  \"indexRules\": {"
+                + "    \"nt:base\": {"
+                + "      \"properties\": {"
+                + "        \"prop1\": { \"name\": \"prop1\", 
\"propertyIndex\": true }"
+                + "      }"
+                + "    }"
+                + "  }"
+                + "} }");
+
+        // expect test-custom-1 with:
+        // - type "lucene" from the product index
+        // - indexRules/nt:base/properties/prop1 from the diff
+        assertSameJson("{\n"
+                + "  \"/oak:index/test-custom-1\": {\n"
+                + "    \"mergeChecksum\": 
\"2e4b9003683d6c179c72f94393a91b2bedc3fa67d1a60f6f7876002dbb40c070\",\n"
+                + "    \"mergeInfo\": \"This index was auto-merged. See also 
https://oak-indexing.github.io/oakTools/simplified.html\",\n";
+                + "    \"reindex\": true,\n"
+                + "    \"jcr:primaryType\": 
\"nam:oak:QueryIndexDefinition\",\n"
+                + "    \"type\": \"lucene\",\n"
+                + "    \"merges\": [\"/oak:index/test\"],\n"
+                + "    \"indexRules\": {\n"
+                + "      \"jcr:primaryType\": \"nam:nt:unstructured\",\n"
+                + "      \"nt:base\": {\n"
+                + "        \"jcr:primaryType\": \"nam:nt:unstructured\",\n"
+                + "        \"properties\": {\n"
+                + "          \"jcr:primaryType\": \"nam:nt:unstructured\",\n"
+                + "          \"prop1\": {\n"
+                + "            \"name\": \"prop1\",\n"
+                + "            \"propertyIndex\": true,\n"
+                + "            \"jcr:primaryType\": \"nam:nt:unstructured\"\n"
+                + "          }\n"
+                + "        }\n"
+                + "      }\n"
+                + "    }\n"
+                + "  },\n"
+                + "  \"/oak:index/test\": {\n"
+                + "    \"reindex\": true,\n"
+                + "    \"jcr:primaryType\": 
\"nam:oak:QueryIndexDefinition\",\n"
+                + "    \"type\": \"lucene\"\n"
+                + "  }\n"
+                + "}",
+                RootIndexesListService.getRootIndexDefinitions(store, 
"lucene").toString());
+    }
+
+    @Test
+    public void testDiffIndexUpdateUnversionedOotb() throws Exception {
+        NodeStore store = new MemoryNodeStore(INITIAL_CONTENT);
+
+        // Seed the store with an unversioned OOTB index "/oak:index/test"
+        NodeBuilder builder = store.getRoot().builder();
+        NodeBuilder indexDefs = builder.child(INDEX_DEFINITIONS_NAME);
+        indexDefs.child("test")
+                .setProperty("jcr:primaryType", 
IndexConstants.INDEX_DEFINITIONS_NODE_TYPE, Type.NAME)
+                .setProperty(TYPE_PROPERTY_NAME, "lucene");
+        store.merge(builder, new EditorHook(new IndexUpdateProvider(
+                CompositeIndexEditorProvider.compose(List.of(
+                        new ReferenceEditorProvider(),
+                        new PropertyIndexEditorProvider(),
+                        new NodeCounterEditorProvider())))),
+                CommitInfo.EMPTY);
+
+        // Apply a diff that adds indexRules to the unversioned index
+        storeDiff(store, "2026-01-01T00:00:00.000Z",
+                "{ \"test\": {"
+                + "  \"indexRules\": {"
+                + "    \"nt:base\": {"
+                + "      \"properties\": {"
+                + "        \"prop1\": { \"name\": \"prop1\", 
\"propertyIndex\": true }"
+                + "      }"
+                + "    }"
+                + "  }"
+                + "} }");
+
+        JsonObject repositoryDefinitions = 
RootIndexesListService.getRootIndexDefinitions(store, "lucene");
+        // expect test-custom-1, not test-0-custom-1
+        JsonObject merged = 
repositoryDefinitions.getChildren().get("/oak:index/test-custom-1");
+        assertNotNull("Expected test-custom-1 to be created, got: " + 
repositoryDefinitions, merged);
+        // property from the product index
+        assertEquals("\"lucene\"", merged.getProperties().get("type"));
+        // indexRules child and prop1 entry from the diff
+        JsonObject prop1 = merged.getChildren().get("indexRules")
+                .getChildren().get("nt:base")
+                .getChildren().get("properties")
+                .getChildren().get("prop1");
+        assertNotNull("prop1 from diff should be present in the merged index", 
prop1);
+    }
+
     private void assertSameJson(String a, String b) {
         JsonObject ja = JsonObject.fromJson(a, true);
         JsonObject jb = JsonObject.fromJson(b, true);
diff --git 
a/oak-run/src/main/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerUtils.java
 
b/oak-run/src/main/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerUtils.java
index 6a107ee01d..dafc2171fe 100644
--- 
a/oak-run/src/main/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerUtils.java
+++ 
b/oak-run/src/main/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerUtils.java
@@ -413,8 +413,8 @@ public class IndexDefMergerUtils {
                 IndexName latest = n.getLatestCustomized(allNames);
                 IndexName ancestor = n.getLatestProduct(allNames);
                 if (latest != null && ancestor != null) {
-                    if (n.compareTo(latest) <= 0 || n.compareTo(ancestor) <= 
0) {
-                        // ignore older versions of indexes
+                    if (n.isVersioned() && (n.compareTo(latest) <= 0 || 
n.compareTo(ancestor) <= 0)) {
+                        // ignore older versions of indexes (not applicable to 
unversioned indexes)
                         continue;
                     }
                     JsonObject latestCustomized = 
allIndexes.getChildren().get(latest.getNodeName());
diff --git 
a/oak-run/src/test/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerScenariosTest.java
 
b/oak-run/src/test/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerScenariosTest.java
index ac11a2aa00..63d3d8d012 100644
--- 
a/oak-run/src/test/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerScenariosTest.java
+++ 
b/oak-run/src/test/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerScenariosTest.java
@@ -57,7 +57,9 @@ public class IndexDefMergerScenariosTest extends 
ParameterizedMergingTestBase {
                 testCase("should not remove child nodes from product index 
missing from custom index",
                         "missing-child.json"),
                 testCase("should support removing adding changing properties 
from product index in custom index",
-                        "removed-property.json")
+                        "removed-property.json"),
+                testCase("should merge customization of unversioned index into 
test-custom-1",
+                        "unversioned-index.json")
         });
     }
 
diff --git 
a/oak-run/src/test/resources/org/apache/jackrabbit/oak/index/merge/unversioned-index.json
 
b/oak-run/src/test/resources/org/apache/jackrabbit/oak/index/merge/unversioned-index.json
new file mode 100644
index 0000000000..6fee8af5b3
--- /dev/null
+++ 
b/oak-run/src/test/resources/org/apache/jackrabbit/oak/index/merge/unversioned-index.json
@@ -0,0 +1,42 @@
+{
+  "run": {
+    "/oak:index/test": {
+      "type": "lucene"
+    },
+    "/oak:index/test-custom-1": {
+      "type": "lucene",
+      "indexRules": {
+        "nt:base": {
+          "properties": {
+            "prop1": {
+              "name": "prop1"
+            }
+          }
+        }
+      }
+    }
+  },
+  "build": {
+    "/oak:index/test": {
+      "type": "lucene"
+    }
+  },
+  "expected": {
+    "/oak:index/test": {
+      "type": "lucene"
+    },
+    "/oak:index/test-custom-1": {
+      "type": "lucene",
+      "merges": ["/oak:index/test", "/oak:index/test-custom-1"],
+      "indexRules": {
+        "nt:base": {
+          "properties": {
+            "prop1": {
+              "name": "prop1"
+            }
+          }
+        }
+      }
+    }
+  }
+}

Reply via email to