diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
index ba2a30b03d..b7cc5bb0d2 100644
--- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
+++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
@@ -184,6 +184,11 @@
+
+
+
+
+
@@ -1433,4 +1438,46 @@
order by childNode.id DESC
+
+
+
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAO.java b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
index 4939ec6294..c880d10796 100644
--- a/source/java/org/alfresco/repo/domain/node/NodeDAO.java
+++ b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
@@ -702,6 +702,19 @@ public interface NodeDAO extends NodeBulkLoader
final QName assocTypeQName,
ChildAssocRefQueryCallback resultsCallback);
+ /**
+ * @param parentNodeId the parent node id
+ * @param minNodeId the minimum node ID (inclusive), null for no limitation on the minimum value of the node id
+ * @param maxNodeId the maximum node ID (exclusive), null for no limitation on the maximum value of the node id
+ * @param assocToExcludeTypeQNames the node associations to exclude, null for no filtering of the associations types
+ * @return list of child nodes
+ */
+ public List selectChildAssocsWithoutNodeAssocsOfTypes(
+ final Long parentNodeId,
+ final Long minNodeId,
+ final Long maxNodeId,
+ final Set assocToExcludeTypeQNames);
+
/**
* Finds the association between the node's primary parent and the node itself
*
@@ -873,6 +886,16 @@ public interface NodeDAO extends NodeBulkLoader
*/
public Long getMaxNodeId();
+ /**
+ * Returns the [minId, maxId] interval for nodes of a type, with the transaction time in the given window time.
+ *
+ * @param type the node type
+ * @param startTxnTime the starting transaction time, null is allowed, case in which no minimum transaction time is considered
+ * @param endTxnTime the end transaction time, null is allowed, case in which no maximum transaction time is considered
+ * @return the interval, as a pair
+ */
+ public Pair getNodeIdsIntervalForType(QName type, Long startTxnTime, Long endTxnTime);
+
/**
* Select children by property values
*/
diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
index 073eead688..b38c13916e 100644
--- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
@@ -114,6 +114,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
private static final String DELETE_NODE_PROPERTIES = "alfresco.node.delete_NodeProperties";
private static final String SELECT_NODE_MIN_ID = "alfresco.node.select_NodeMinId";
private static final String SELECT_NODE_MAX_ID = "alfresco.node.select_NodeMaxId";
+ private static final String SELECT_NODE_INTERVAL_BY_TYPE = "alfresco.node.select_MinMaxNodeIdForNodeType";
private static final String SELECT_NODES_WITH_ASPECT_IDS = "alfresco.node.select_NodesWithAspectIds";
private static final String INSERT_NODE_ASSOC = "alfresco.node.insert.insert_NodeAssoc";
private static final String UPDATE_NODE_ASSOC = "alfresco.node.update_NodeAssoc";
@@ -138,6 +139,8 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
private static final String SELECT_CHILD_ASSOC_OF_PARENT_BY_NAME = "alfresco.node.select_ChildAssocOfParentByName";
private static final String SELECT_CHILD_ASSOCS_OF_PARENT_WITHOUT_PARENT_ASSOCS_OF_TYPE =
"alfresco.node.select_ChildAssocsOfParentWithoutParentAssocsOfType";
+ private static final String SELECT_CHILD_ASSOCS_OF_PARENT_WITHOUT_NODE_ASSOCS_OF_TYPE =
+ "alfresco.node.select_ChildAssocsOfParentWithoutNodeAssocsOfType";
private static final String SELECT_PARENT_ASSOCS_OF_CHILD = "alfresco.node.select_ParentAssocsOfChild";
private static final String UPDATE_PARENT_ASSOCS_OF_CHILD = "alfresco.node.update_ParentAssocsOfChild";
private static final String DELETE_SUBSCRIPTIONS = "alfresco.node.delete_NodeSubscriptions";
@@ -373,6 +376,38 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
return (Long) template.selectOne(SELECT_NODE_MAX_ID);
}
+ @Override
+ public Pair getNodeIdsIntervalForType(QName type, Long startTxnTime, Long endTxnTime)
+ {
+ final Pair intervalPair = new Pair(LONG_ZERO, LONG_ZERO);
+ Pair typePair = qnameDAO.getQName(type);
+ if (typePair == null)
+ {
+ // Return default
+ return intervalPair;
+ }
+ TransactionQueryEntity txnQuery = new TransactionQueryEntity();
+ txnQuery.setTypeQNameId(typePair.getFirst());
+ txnQuery.setMinCommitTime(startTxnTime);
+ txnQuery.setMaxCommitTime(endTxnTime);
+
+ ResultHandler resultHandler = new ResultHandler()
+ {
+ @SuppressWarnings("unchecked")
+ public void handleResult(ResultContext context)
+ {
+ Map result = (Map) context.getResultObject();
+ if (result != null)
+ {
+ intervalPair.setFirst(result.get("minId"));
+ intervalPair.setSecond(result.get("maxId"));
+ }
+ }
+ };
+ template.select(SELECT_NODE_INTERVAL_BY_TYPE, txnQuery, resultHandler);
+ return intervalPair;
+ }
+
@Override
protected void updatePrimaryChildrenSharedAclId(
Long txnId,
@@ -1385,7 +1420,30 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
resultsCallback.done();
}
- @SuppressWarnings("unchecked")
+ @Override
+ public List selectChildAssocsWithoutNodeAssocsOfTypes(Long parentNodeId, Long minNodeId, Long maxNodeId, Set assocTypeQNames)
+ {
+ IdsEntity idsEntity = new IdsEntity();
+
+ // Parent node id
+ Assert.notNull(parentNodeId, "The parent node id must not be null.");
+ idsEntity.setIdOne(parentNodeId);
+ // Node ids selection interval
+ idsEntity.setIdTwo(minNodeId);
+ idsEntity.setIdThree(maxNodeId);
+ // Associations types to exclude
+ if (assocTypeQNames != null)
+ {
+ Set childNodeTypeQNameIds = qnameDAO.convertQNamesToIds(assocTypeQNames, false);
+ if (childNodeTypeQNameIds.size() > 0)
+ {
+ idsEntity.setIds(new ArrayList(childNodeTypeQNameIds));
+ }
+ }
+
+ return template.selectList(SELECT_CHILD_ASSOCS_OF_PARENT_WITHOUT_NODE_ASSOCS_OF_TYPE, idsEntity);
+ }
+
@Override
protected List selectPrimaryParentAssocs(Long childNodeId)
{
@@ -1665,7 +1723,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
if (deletedTypePair == null)
{
// Nothing to do
- return 0L;
+ return LONG_ZERO;
}
TransactionQueryEntity txnQuery = new TransactionQueryEntity();
txnQuery.setTypeQNameId(deletedTypePair.getFirst());
diff --git a/source/test-java/org/alfresco/repo/domain/node/NodeDAOTest.java b/source/test-java/org/alfresco/repo/domain/node/NodeDAOTest.java
index 646239013f..067b48ab9f 100644
--- a/source/test-java/org/alfresco/repo/domain/node/NodeDAOTest.java
+++ b/source/test-java/org/alfresco/repo/domain/node/NodeDAOTest.java
@@ -189,6 +189,79 @@ public class NodeDAOTest extends TestCase
}
}
+ public void testGetNodeIdsIntervalForType() throws Exception
+ {
+ // Different calls with equivalent parameters should return the same values
+ Pair interval1 = getNodeIdsInterval(0L, System.currentTimeMillis());
+ Pair interval2 = getNodeIdsInterval(null, System.currentTimeMillis());
+ Pair interval3 = getNodeIdsInterval(null, null);
+
+ assertEquals(interval1.getFirst(), interval2.getFirst());
+ assertEquals(interval2.getFirst(), interval3.getFirst());
+
+ assertEquals(interval1.getSecond(), interval2.getSecond());
+ assertEquals(interval2.getSecond(), interval3.getSecond());
+ }
+
+ private Pair getNodeIdsInterval(final Long minTxnTime, final Long maxTxnTime)
+ {
+ RetryingTransactionCallback> callback = new RetryingTransactionCallback>()
+ {
+ public Pair execute() throws Throwable
+ {
+ return nodeDAO.getNodeIdsIntervalForType(ContentModel.TYPE_FOLDER, minTxnTime, maxTxnTime);
+ }
+ };
+
+ return txnHelper.doInTransaction(callback, true);
+ }
+
+ public void testSelectChildAssocsWithoutNodeAssocsOfTypes()
+ {
+ final StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
+ final Long parentId = nodeDAO.getRootNode(storeRef).getFirst();
+
+ final AtomicLong min = new AtomicLong(0L);
+ final AtomicLong max = new AtomicLong(0L);
+ final Set typeAssocsToExclude = Collections.singleton(QName.createQName("noType"));
+
+ RetryingTransactionCallback> callback = new RetryingTransactionCallback>()
+ {
+ public List execute() throws Throwable
+ {
+ long minNodeId = min.get();
+ long maxNodeId = max.get();
+ return nodeDAO.selectChildAssocsWithoutNodeAssocsOfTypes(parentId, minNodeId, maxNodeId, typeAssocsToExclude);
+ }
+ };
+
+ // Get the current node range
+ Pair nodeRange = getNodeIdsInterval(0L, System.currentTimeMillis());
+
+ Long minNodeId = nodeRange.getFirst();
+ Long maxNodeId = nodeRange.getSecond();
+
+ min.set(minNodeId.longValue());
+
+ // Iterate across the nodes in the [minNodeId, maxNodeId] interval
+ while (min.longValue() <= maxNodeId.longValue())
+ {
+ max.set(min.get() + 100L); // 100 increments
+ // Get the nodes
+ List nodes = txnHelper.doInTransaction(callback, true);
+ for (Node node : nodes)
+ {
+ Long nodeId = node.getId();
+ assertNotNull(nodeId);
+ assertTrue("the min should be inclusive.", min.longValue() <= nodeId.longValue());
+ assertTrue("the max should be exclusive.", max.longValue() > nodeId.longValue());
+ }
+
+ // Shift the window up
+ min.set(max.get());
+ }
+ }
+
public void testGetNodesWithAspects() throws Throwable
{
final NodeRefQueryCallback callback = new NodeRefQueryCallback()