DocumentOptimizer implements DocumentConstants { public boolean optimizeChildrenBlocks( NodeKey key, EditableDocument document, int targetCountPerBlock, int tolerance ) { if (document == null) { document = edit(key.toString()); if (document == null) { return false; } } EditableArray children = document.getArray(CHILDREN); if (children == null) { return false; } EditableDocument info = document.getDocument(CHILDREN_INFO); boolean selfContained = true; if (info != null) { selfContained = !info.containsField(NEXT_BLOCK); } boolean changed = false; if (selfContained) { int total = children.size(); if (total < targetCountPerBlock + tolerance) { return false; } splitChildren(key, document, children, targetCountPerBlock, tolerance, true, null); changed = true; } else { assert info != null; EditableDocument doc = document; NodeKey docKey = key; while (doc != null) { EditableDocument docInfo = doc.getDocument(CHILDREN_INFO); String nextKey = docInfo != null ? docInfo.getString(NEXT_BLOCK) : null; children = doc.getArray(CHILDREN); int count = children.size(); boolean isFirst = doc == document; if (count > (targetCountPerBlock + tolerance)) { splitChildren(docKey, doc, children, targetCountPerBlock, tolerance, isFirst, nextKey); changed = true; } else if (count < (targetCountPerBlock - tolerance) && nextKey != null) { nextKey = mergeChildren(docKey, doc, children, isFirst, nextKey); changed = true; if (nextKey == null) { info.setString(LAST_BLOCK, docKey.toString()); } } if (nextKey != null) { doc = edit(nextKey); docKey = new NodeKey(nextKey); } else { doc = null; } } } return changed; } DocumentOptimizer( DocumentStore documentStore ); boolean optimizeChildrenBlocks( NodeKey key,
                                           EditableDocument document,
                                           int targetCountPerBlock,
                                           int tolerance );  }
@Test public void shouldSplitDocumentThatRepeatedlyContainsTooManyChildReferencesIntoMultipleSegments() throws Exception { MutableCachedNode nodeB = check(session1).mutableNode("/childB"); NodeKey key = nodeB.getKey(); runInTransaction(() -> optimizer.optimizeChildrenBlocks(key, null, 5, 2)); session1.save(); nodeB = check(session1).mutableNode("/childB"); print(false); print(document(key), true); print(false); for (int j = 0; j != 5; ++j) { for (int i = 0; i != 5; ++i) { NodeKey newKey = key.withId("child" + ((j * 5) + i + 1)); nodeB.createChild(session(), newKey, name("newChild"), property("p1a", 344), property("p2", false)); } session1.save(); nodeB = check(session1).mutableNode("/childB"); print(false); print("\nOptimizing..."); print(document(key), true); runInTransaction(() -> optimizer.optimizeChildrenBlocks(key, null, 5, 2)); print("\nOptimized..."); print(document(key), true); print(false); } runInTransaction(() -> optimizer.optimizeChildrenBlocks(key, null, 5, 2)); print(false); print(document(key), true); }