This is an automated email from the ASF dual-hosted git repository. thomasm pushed a commit to branch OAK-11534 in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
commit c30991d45d31d3964c144c75aecd2c3db7c4455e Author: Thomas Mueller <thom...@apache.org> AuthorDate: Fri Feb 28 15:38:55 2025 +0100 OAK-11534 Auto-merge indexes if string matches single-value array --- .../oak/index/merge/IndexDefMergerUtils.java | 37 ++++++++++++++++++++++ .../oak/index/merge/IndexDefMergerTest.java | 28 +++++++++++++++- .../apache/jackrabbit/oak/index/merge/merge.txt | 7 ++++ 3 files changed, 71 insertions(+), 1 deletion(-) 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 3736e3c7ea..aade3856e3 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 @@ -31,6 +31,8 @@ import java.util.stream.Collectors; import org.apache.jackrabbit.oak.commons.json.JsonObject; import org.apache.jackrabbit.oak.commons.json.JsopBuilder; +import org.apache.jackrabbit.oak.commons.json.JsopReader; +import org.apache.jackrabbit.oak.commons.json.JsopTokenizer; import org.apache.jackrabbit.oak.plugins.index.IndexName; /** @@ -148,6 +150,8 @@ public class IndexDefMergerUtils { return cp; } else if (Objects.equals(ap, cp)) { return pp; + } else if (equalStringValues(ap, cp)) { + return pp; } else { conflicts.add("Could not merge value; path=" + path + " property=" + property + "; ancestor=" + ap + "; custom=" + cp + "; product=" + pp); @@ -155,6 +159,39 @@ public class IndexDefMergerUtils { } } + public static boolean equalStringValues(String a, String b) { + String aa = getStringOrStringFromSingleValueArray(a); + String bb = getStringOrStringFromSingleValueArray(b); + if (aa == null || bb == null) { + // a or b are not strings or single-valued array of strings + return false; + } + return Objects.equals(aa, bb); + } + + public static String getStringOrStringFromSingleValueArray(String value) { + if (value == null) { + return null; + } + JsopTokenizer tokenizer = new JsopTokenizer(value); + if (tokenizer.matches(JsopReader.STRING)) { + return tokenizer.getEscapedToken(); + } + if (!tokenizer.matches('[')) { + return null; + } + if (!tokenizer.matches(JsopReader.STRING)) { + // not a string + return null; + } + String jsonString = tokenizer.getEscapedToken(); + if (!tokenizer.matches(']')) { + // not a single-element array + return null; + } + return jsonString; + } + private static JsonObject mergeChild(String path, String child, int level, JsonObject ancestor, JsonObject custom, JsonObject product, ArrayList<String> conflicts) { JsonObject a = ancestor.getChildren().get(child); diff --git a/oak-run/src/test/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerTest.java b/oak-run/src/test/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerTest.java index 952cca3042..7a71b1f65e 100644 --- a/oak-run/src/test/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerTest.java +++ b/oak-run/src/test/java/org/apache/jackrabbit/oak/index/merge/IndexDefMergerTest.java @@ -19,6 +19,8 @@ package org.apache.jackrabbit.oak.index.merge; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.io.IOException; import java.io.InputStreamReader; @@ -35,11 +37,35 @@ import org.junit.Test; */ public class IndexDefMergerTest { + @Test + public void compareStringAndSingleValueArray() { + assertEquals(null, IndexDefMergerUtils.getStringOrStringFromSingleValueArray(null)); + assertEquals(null, IndexDefMergerUtils.getStringOrStringFromSingleValueArray("1")); + assertEquals(null, IndexDefMergerUtils.getStringOrStringFromSingleValueArray("[\"a\",2]")); + assertEquals(null, IndexDefMergerUtils.getStringOrStringFromSingleValueArray("[\"a\",null]")); + assertEquals(null, IndexDefMergerUtils.getStringOrStringFromSingleValueArray("[1]")); + assertEquals("", IndexDefMergerUtils.getStringOrStringFromSingleValueArray("\"\"")); + assertEquals("a", IndexDefMergerUtils.getStringOrStringFromSingleValueArray("\"a\"")); + assertEquals("a", IndexDefMergerUtils.getStringOrStringFromSingleValueArray("[\"a\"]")); + assertTrue(IndexDefMergerUtils.equalStringValues("[\"a\"]", "[\"a\"]")); + assertTrue(IndexDefMergerUtils.equalStringValues("[\"a\"]", "\"a\"")); + assertTrue(IndexDefMergerUtils.equalStringValues("\"a\"", "[\"a\"]")); + assertTrue(IndexDefMergerUtils.equalStringValues("\"a\"", "\"a\"")); + assertFalse(IndexDefMergerUtils.equalStringValues("\"a\"", "\"b\"")); + assertFalse(IndexDefMergerUtils.equalStringValues("1", "1")); + assertFalse(IndexDefMergerUtils.equalStringValues("1", "2")); + assertFalse(IndexDefMergerUtils.equalStringValues(null, null)); + assertFalse(IndexDefMergerUtils.equalStringValues("1", null)); + assertFalse(IndexDefMergerUtils.equalStringValues(null, "1")); + assertFalse(IndexDefMergerUtils.equalStringValues("\"1\"", "1")); + assertFalse(IndexDefMergerUtils.equalStringValues("1", "\"1\"")); + } + @Test public void merge() throws IOException, CommitFailedException { String s = readFromResource("merge.txt"); JsonObject json = JsonObject.fromJson(s, true); - for(JsonObject e : array(json.getProperties().get("tests"))) { + for (JsonObject e : array(json.getProperties().get("tests"))) { merge(e); } } diff --git a/oak-run/src/test/resources/org/apache/jackrabbit/oak/index/merge/merge.txt b/oak-run/src/test/resources/org/apache/jackrabbit/oak/index/merge/merge.txt index a6dc62ac44..b767cdeb20 100644 --- a/oak-run/src/test/resources/org/apache/jackrabbit/oak/index/merge/merge.txt +++ b/oak-run/src/test/resources/org/apache/jackrabbit/oak/index/merge/merge.txt @@ -1,5 +1,12 @@ {"tests": [ +{ + "ancestor": {"includedPaths": ["/content/a"]}, + "custom": {"includedPaths": "/content/a"}, + "product": {"includedPaths": ["/content/a", "/content/b"]}, + "expected": {"includedPaths": ["/content/a", "/content/b"], "merges": ["/oak:index/test-2", "/oak:index/test-1-custom-1"] + } +}, { "ancestor": {"value": 1, "a-old": 0}, "custom": {"value": 2, "b-new": 3},