Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java (revision 1668276) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java (working copy) @@ -258,35 +258,49 @@ if (!beforeKeys.isEmpty() || !afterKeys.isEmpty()) { updateCallback.indexUpdate(); NodeBuilder index = definition.child(INDEX_CONTENT_NODE_NAME); - getStrategy(keysToCheckForUniqueness != null).update( - index, getPath(), beforeKeys, afterKeys); if (keysToCheckForUniqueness != null) { - keysToCheckForUniqueness.addAll(afterKeys); + keysToCheckForUniqueness + .addAll(filterFailingUniques(afterKeys, index, false)); } + getStrategy(keysToCheckForUniqueness != null).update( + index, getPath(), beforeKeys, afterKeys); } } if (parent == null) { // make sure that the index node exist, even with no content - definition.child(INDEX_CONTENT_NODE_NAME); + NodeBuilder index = definition.child(INDEX_CONTENT_NODE_NAME); // check uniqueness constraints when leaving the root if (keysToCheckForUniqueness != null && !keysToCheckForUniqueness.isEmpty()) { - NodeState indexMeta = definition.getNodeState(); - IndexStoreStrategy s = getStrategy(true); - for (String key : keysToCheckForUniqueness) { - if (s.count(indexMeta, singleton(key), 2) > 1) { - String msg = String.format("Uniqueness constraint violated at path [%s] for one of the " + - "property in %s having value %s", getPath(), propertyNames, key); - throw new CommitFailedException( - CONSTRAINT, 30, msg); - } + Set failed = filterFailingUniques( + keysToCheckForUniqueness, index, true); + if (!failed.isEmpty()) { + String msg = String.format( + "Uniqueness constraint violated at path [%s] for one of the " + + "property in %s having value %s", + getPath(), propertyNames, failed.iterator().next()); + throw new CommitFailedException(CONSTRAINT, 30, msg); } } } } + private Set filterFailingUniques(Set keys, NodeBuilder index, boolean fastStop) { + Set doubles = newHashSet(); + IndexStoreStrategy s = getStrategy(true); + for (String key : keys) { + if (s.exists(index, key)) { + doubles.add(key); + if (fastStop) { + return doubles; + } + } + } + return doubles; + } + private static boolean isTypeProperty(String name) { return JCR_PRIMARYTYPE.equals(name) || JCR_MIXINTYPES.equals(name); } Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (revision 1668276) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (working copy) @@ -460,4 +460,35 @@ } } } + + @Override + public boolean exists(NodeBuilder index, String key) { + NodeBuilder builder = index.getChildNode(key); + if (builder.exists()) { + // check for empty leaves + + // Collect all builders along the given path + Deque builders = newArrayDeque(); + builders.addFirst(builder); + + while (true) { + NodeBuilder b = builders.poll(); + if (b == null) { + return false; + } + + for (String n : b.getChildNodeNames()) { + NodeBuilder c = b.getChildNode(n); + if (c.hasProperty("match")) { + return true; + } else { + builders.push(c); + } + } + } + + } else { + return false; + } + } } \ No newline at end of file Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java (revision 1668276) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/IndexStoreStrategy.java (working copy) @@ -39,7 +39,9 @@ void update( NodeBuilder index, String path, Set beforeKeys, Set afterKeys); - + + boolean exists(NodeBuilder index, String key); + /** * Search for a given set of values. * Index: src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java (revision 1668276) +++ src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java (working copy) @@ -166,4 +166,9 @@ return count; } + @Override + public boolean exists(NodeBuilder index, String key) { + return index.hasChildNode(key); + } + }