childNodePair
                );
        
        boolean preLoadNodes();
    }
    /**
     * Interface used to iterate over results from child association queries
     * @author Derek Hulley
     */
    public interface ChildAssocRefQueryCallbackFilter extends ChildAssocRefQueryCallback
    {
        /**
         * Method to handle raw query results and decide if the result should be filtered out or not.
         * If true is returned, the standard {@link #handle(Pair, Pair, Pair) handler} method
         * will be called.  If false is returned, then the query result will be skipped.
         * 
         * This provides a quick way to filter out results without having to pull in full entities.
         * 
         * @return              Return true if the standard {@link #handle(Pair, Pair, Pair) handler}
         *                      method should be called, or false to filter the result out.
         */
        boolean isDesiredRow(
                Pair childAssocPair,
                Pair parentNodePair,
                Pair childNodePair,
                String assocChildNodeName,
                Long assocChildNodeNameCrc
                );
    }
    /**
     * Get a collection of all child association references for a given parent node.
     * 
     * WARNING: Be sure selective when doing this call recursively.
     * 
     * @param parentNodeId          the parent node
     * @param resultsCallback       the callback that will be called with the results
     * @param recurse               if true then iterate over the entire tree of nodes.
     *                              Resursion is done top-down i.e. the first level children are all
     *                              enumerated first, followed by all second level children and so on.
     */
    @DirtySessionAnnotation(markDirty=false)
    public void getChildAssocs(Long parentNodeId, ChildAssocRefQueryCallback resultsCallback, boolean recurse);
    
    /**
     * Get a collection of all child association references for a given parent node.
     * 
     * @param parentNodeId          the parent node
     * @param resultsCallback       the callback that will be called with the results
     */
    @DirtySessionAnnotation(markDirty=false)
    public void getChildAssocs(Long parentNodeId, QName assocQName, ChildAssocRefQueryCallback resultsCallback);
    /**
     * Get a collection of all child associations references where the child name is an exact match.
     * This method only works if the association type fundamentally supports unique-name enforcement.
     * 
     * @param parentNodeId          the parent node
     * @param assocTypeQName        the type of the association to check.
     * @param childNames            the names of the child nodes (cm:name).  These will be matched exactly.
     * @param resultsCallback       the callback that will be called with the results
     */
    @DirtySessionAnnotation(markDirty=false)
    public void getChildAssocs(Long parentNodeId, QName assocTypeQName, Collection childNames, ChildAssocRefQueryCallback resultsCallback);
    
    @DirtySessionAnnotation(markDirty=false)
    public void getChildAssocsByTypeQNames(
            Long parentNodeId,
            List assocTypeQNames,
            ChildAssocRefQueryCallback resultsCallback);
    
    @DirtySessionAnnotation(markDirty=false)
    public void getChildAssocsByTypeQNameAndQName(
            Long parentNodeId,
            QName assocTypeQName,
            QName assocQName,
            ChildAssocRefQueryCallback resultsCallback);
    
    @DirtySessionAnnotation(markDirty=false)
    public void getChildAssocsByChildTypes(
            Long parentNodeId,
            Set childNodeTypeQNames,
            ChildAssocRefQueryCallback resultsCallback);
    
    @DirtySessionAnnotation(markDirty=false)
    public void getPrimaryChildAssocs(Long parentNodeId, ChildAssocRefQueryCallback resultsCallback);
    
    @DirtySessionAnnotation(markDirty=false)
    public void getPrimaryChildAssocsNotInSameStore(Long parentNodeId, ChildAssocRefQueryCallback resultsCallback);
    
    /**
     * Interface used to iterate over pure node results
     * @author Derek Hulley
     */
    public interface NodeRefQueryCallback
    {
        /**
         * 
         * @param nodePair          the node result
         * @return                  Returns true if more results are required
         */
        boolean handle(Pair nodePair);
    }
    
    /**
     * Gets a set of nodes that have parents in the given store, but are themselves located in a different
     * store.
     * 
     * @param storeId               the store of the parent nodes
     * @param minNodeId             the min node ID to return
     * @param count                 the maximum number of results
     * @param resultsCallback       the node callback
     * 
     * @deprecated                  Since 2.2SP6, 3.1SP2, 3.2: not performant and not relevant to any use-cases
     */
    @DirtySessionAnnotation(markDirty=false)
    public void getNodesWithChildrenInDifferentStore(
            Long storeId,
            Long minNodeId,
            int count,
            NodeRefQueryCallback resultsCallback);
    
    @DirtySessionAnnotation(markDirty=false)
    public void getNodesWithAspect(QName aspectQName, Long minNodeId, int count, NodeRefQueryCallback resultsCallback);
    
    /**
     * Gets the set of child associations of a certain parent node without parent associations of a certain type to
     * other nodes with the same parent! In effect the 'orphans' with respect to a certain association type.
     * 
     * @param parentNodeId
     *            the parent node ID
     * @param assocTypeQName
     *            the association type QName
     * @param resultsCallback
     *            the callback that will be called with the results
     */
    @DirtySessionAnnotation(markDirty = false)
    public void getChildAssocsWithoutParentAssocsOfType(final Long parentNodeId, final QName assocTypeQName,
            ChildAssocRefQueryCallback resultsCallback);
    /**
     * @return Returns an association matching the given parent, type and child name (cm:name) - or null if not found
     */
    @DirtySessionAnnotation(markDirty=false)
    public Pair getChildAssoc(Long parentNodeId, QName assocTypeQName, String childName);
    
    /**
     * @return Returns a matching association or null if one was not found
     * 
     * @see ChildAssoc
     */
    @DirtySessionAnnotation(markDirty=false)
    public Pair getChildAssoc(
            Long parentNodeId,
            Long childNodeId,
            QName assocTypeQName,
            QName qname);
    /**
     * Deletes an explicit child association.
     * 
     * @return Returns true if the association was deleted, otherwise false
     */
    @DirtySessionAnnotation(markDirty=true)
    public boolean deleteChildAssoc(
            final Long parentNodeId,
            final Long childNodeId,
            final QName assocTypeQName,
            final QName qname);
    
    /**
     * @param assoc the child association to remove
     */
    @DirtySessionAnnotation(markDirty=true)
    public void deleteChildAssoc(Long childAssocId);
    
    /**
     * Finds the association between the node's primary parent and the node itself
     */
    @DirtySessionAnnotation(markDirty=false)
    public Pair getPrimaryParentAssoc(Long childNodeId);
    
    /**
     * Get all parent associations for the node.  This methods includes a cache safety check.
     * @param childNode the child node
     * @return Returns all parent associations for the node.
     */
    @DirtySessionAnnotation(markDirty=false)
    public Collection> getParentAssocs(final Long childNodeId);
    
    /**
     * @return Returns the persisted and filled association
     * @see NodeAssoc
     */
    @DirtySessionAnnotation(markDirty=true)
    public Pair newNodeAssoc(
            Long sourceNodeId,
            Long targetNodeId,
            QName assocTypeQName);
    
    /**
     * @return Returns a list of all node associations associated with the given node
     */
    @DirtySessionAnnotation(markDirty=false)
    public Collection> getNodeAssocsToAndFrom(final Long nodeId);
    /**
     * @return Returns the node association or null if not found
     */
    @DirtySessionAnnotation(markDirty=false)
    public Pair getNodeAssoc(Long sourceNodeId, Long targetNodeId, QName assocTypeQName);
    
    /**
     * Gets a node association by ID.
     * 
     * @param assocId
     *            the association id
     * @return the node association, or null if it does not exist
     */
    @DirtySessionAnnotation(markDirty = false)
    public AssociationRef getNodeAssocOrNull(Long assocId);
    /**
     * @return Returns all the node associations where the node is the source
     */
    @DirtySessionAnnotation(markDirty=false)
    public Collection> getTargetNodeAssocs(Long sourceNodeId);
    
    /**
     * @return Returns all the node associations where the node is the target
     */
    @DirtySessionAnnotation(markDirty=false)
    public Collection> getSourceNodeAssocs(Long targetNodeId);
    
    /**
     * @param assoc the node association to remove
     */
    @DirtySessionAnnotation(markDirty=true)
    public void deleteNodeAssoc(Long assocId);
    
    /**
     * Iterate over all nodes that have a given property type with a given string value.
     * 
     * @param storeRef                          the store to search in
     * @param propertyQName                     the qualified name of the property
     * @param value                             the string value to match
     * @param handler                           the callback to use while iterating over the URLs
     * @return Returns the values for the given type definition
     */
    @DirtySessionAnnotation(markDirty=true)
    public void getPropertyValuesByPropertyAndValue(
            StoreRef storeRef,
            QName propertyQName,
            String value,
            NodePropertyHandler handler);
    
    /**
     * Interface used to iterate over object array results
     */
    public interface ObjectArrayQueryCallback
    {
        boolean handle(Object[] array);
    }
    
    /**
     * Iterate over all content nodes to get owner/creator and content url (in order to extract content size)
     * 
     * @param storeRef                          the store to search in
     * @param handler                           the callback to use while iterating over the URLs
     * @return Returns the values for the given owner, creator and content url
     */
    @DirtySessionAnnotation(markDirty=true)
    public void getContentUrlsForStore(
            StoreRef storeRef,
            ObjectArrayQueryCallback resultsCallback);
    
    /**
     * Iterate over all person nodes with missing usage property (for one-off patch)
     * 
     * @param storeRef                          the store to search in
     * @param handler                           the callback to use while iterating over the people
     * @return Returns the values for person node uuid
     */
    @DirtySessionAnnotation(markDirty=true)
    public void getUsersWithoutUsageProp(
            StoreRef storeRef,
            ObjectArrayQueryCallback resultsCallback);
    
    
    /**
     * Iterate over all person nodes to get users without a calculated usage
     * 
     * @param storeRef                          the store to search in
     * @param handler                           the callback to use while iterating over the people
     * @return Returns the values for username and person node uuid (excluding System)
     */
    @DirtySessionAnnotation(markDirty=true)
    public void getUsersWithoutUsage(
            StoreRef storeRef,
            ObjectArrayQueryCallback resultsCallback);
    
    /**
     * Iterate over all person nodes to get users with a calculated usage
     * 
     * @param storeRef                          the store to search in
     * @param handler                           the callback to use while iterating over the people
     * @return Returns the values for the username and person node uuid (excluding System)
     */
    @DirtySessionAnnotation(markDirty=true)
    public void getUsersWithUsage(
            StoreRef storeRef,
            ObjectArrayQueryCallback resultsCallback);
       
    /**
     * Iterate over all property values for the given type definition.  This will also dig out values that
     * were persisted as type d:any.
     * 
     * @param actualDataTypeDefinition          the persisted type to retrieve
     * @param handler                           the callback to use while iterating over the URLs
     * @return Returns the values for the given type definition
     */
    @DirtySessionAnnotation(markDirty=true)
    public void getPropertyValuesByActualType(DataTypeDefinition actualDataTypeDefinition, NodePropertyHandler handler);
    
    /**
     * Gets a batch of deleted nodes in old transactions.
     * 
     * @param minNodeId                     the minimum node ID
     * @param maxCommitTime                 the maximum commit time (to set a minimum transaction age)
     * @param count                         the maximum number of results (for batching)
     * @param resultsCallback               the callback to pass results back
     */
    @DirtySessionAnnotation(markDirty=false)
    public void getNodesDeletedInOldTxns(Long minNodeId, long maxCommitTime, int count, NodeRefQueryCallback resultsCallback);
    
    /**
     * Iterface to handle callbacks when iterating over properties
     * 
     * @author Derek Hulley
     * @since 2.0
     */
    public interface NodePropertyHandler
    {
        void handle(NodeRef nodeRef, QName nodeTypeQName, QName propertyQName, Serializable value);
    }
    
    /**
     * Retrieves the maximum transaction ID for which the commit time is less than the given time.
     * 
     * @param maxCommitTime         the max commit time (ms)
     * @return                      the last transaction on or before the given time
     */
    @DirtySessionAnnotation(markDirty=true)
    public Long getMaxTxnIdByCommitTime(final long maxCommitTime);
    /**
     * Retrieves a specific transaction.
     * 
     * @param txnId                 the unique transaction ID.
     * @return                      the requested transaction or null
     */
    @DirtySessionAnnotation(markDirty=true)
    public Transaction getTxnById(long txnId);
    /**
     * Get all transactions in a given time range.  Since time-based retrieval doesn't guarantee uniqueness
     * for any given millisecond, a list of optional exclusions may be provided.
     * 
     * @param excludeTxnIds         a list of txn IDs to ignore.  null is allowed.
     * @param remoteOnly            true if locally-written transactions must be ignored
     */
    @DirtySessionAnnotation(markDirty=true)
    public List getTxnsByCommitTimeAscending(
            long fromTimeInclusive,
            long toTimeExclusive,
            int count,
            List excludeTxnIds,
            boolean remoteOnly);
    /**
     * Get all transactions in a given time range.  Since time-based retrieval doesn't guarantee uniqueness
     * for any given millisecond, a list of optional exclusions may be provided.
     * 
     * @param excludeTxnIds         a list of txn IDs to ignore.  null is allowed.
     * @param remoteOnly            true if locally-written transactions must be ignored
     */
    @DirtySessionAnnotation(markDirty=true)
    public List getTxnsByCommitTimeDescending(
            long fromTimeInclusive,
            long toTimeExclusive,
            int count,
            List excludeTxnIds,
            boolean remoteOnly);
    /**
     * Get the lowest commit time for a set of transactions
     * 
     * @param includeTxnIds     a list of transaction IDs to search for
     * @return      Returns the transactions by commit time for the given IDs
     */
    @DirtySessionAnnotation(markDirty=true)
    public List getTxnsByMinCommitTime(List includeTxnIds);
    
    @DirtySessionAnnotation(markDirty=false)
    public int getTxnUpdateCount(final long txnId);
    @DirtySessionAnnotation(markDirty=false)
    public int getTxnDeleteCount(final long txnId);
    
    @DirtySessionAnnotation(markDirty=false)
    public int getTransactionCount();
    
    @DirtySessionAnnotation(markDirty=false)
    public List getTxnChangesForStore(final StoreRef storeRef, final long txnId);
    
    @DirtySessionAnnotation(markDirty=false)
    public List getTxnChanges(final long txnId);
    
    @DirtySessionAnnotation(markDirty=false)
    public List getTxnsUnused(Long minTxnId, long maxCommitTime, int count);
    
    @DirtySessionAnnotation(markDirty=true)
    public void purgeTxn(Long txnId);
    
    @DirtySessionAnnotation(markDirty=false)
    public Long getMinTxnCommitTime();
    
    @DirtySessionAnnotation(markDirty=false)
    public Long getMaxTxnCommitTime();
    @DirtySessionAnnotation(markDirty=false)
    public void prependPaths(Pair currentNodePair, Pair currentRootNodePair,
            Path currentPath, Collection completedPaths, Stack assocIdStack, boolean primaryOnly)
            throws CyclicChildRelationshipException;
}