[MNT-25404] Query Performance - High performance cost in retrieving nodes/node properties for large result sets

- pre-commit changes
This commit is contained in:
Jared Ottley
2025-11-02 18:44:09 -07:00
parent 8bc7937e98
commit d9ff7ef1d0
4 changed files with 406 additions and 461 deletions

View File

@@ -46,6 +46,7 @@ public interface DeletedNodes
{
/**
* Lists deleted nodes using a ArchivedNodesCannedQuery
*
* @param parameters
* @return Collection of deleted Nodes
*/
@@ -53,9 +54,11 @@ public interface DeletedNodes
/**
* Gets a single deleted node by id.
*
* @param originalId
* @param parameters
* @param fullnode Should we return the full representation of the minimal one?
* @param fullnode
* Should we return the full representation of the minimal one?
* @param mapUserInfo
* @return a deleted node
*/
@@ -73,7 +76,7 @@ public interface DeletedNodes
* @return a deleted node
*/
List<Node> getDeletedNodes(List<String> originalIds, Parameters parameters, boolean fullnode, Map<String, UserInfo> mapUserInfo);
/**
* Restores a deleted node and returns it.
*
@@ -86,6 +89,7 @@ public interface DeletedNodes
/**
* Permanently delete the node.
*
* @param archivedId
*/
void purgeArchivedNode(String archivedId);
@@ -116,13 +120,16 @@ public interface DeletedNodes
CollectionWithPagingInfo<Rendition> getRenditions(String archivedId, Parameters parameters);
/**
* Gets a presigned URL to directly access content.
* Gets a presigned URL to directly access content.
*
* @param archivedId The node id for which to obtain the direct access {@code URL}
* @param renditionId The rendition id for which to obtain the direct access {@code URL}
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}, {@code true} by default.
* @return A direct access {@code URL} object for the content.
*/
* @param archivedId
* The node id for which to obtain the direct access {@code URL}
* @param renditionId
* The rendition id for which to obtain the direct access {@code URL}
* @param attachment
* {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}, {@code true} by default.
* @return A direct access {@code URL} object for the content.
*/
default DirectAccessUrl requestContentDirectUrl(String archivedId, String renditionId, boolean attachment)
{
return requestContentDirectUrl(archivedId, renditionId, attachment, null);
@@ -131,10 +138,14 @@ public interface DeletedNodes
/**
* Gets a presigned URL to directly access content.
*
* @param archivedId The node id for which to obtain the direct access {@code URL}
* @param renditionId The rendition id for which to obtain the direct access {@code URL}
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}, {@code true} by default.
* @param validFor The time at which the direct access {@code URL} will expire.
* @param archivedId
* The node id for which to obtain the direct access {@code URL}
* @param renditionId
* The rendition id for which to obtain the direct access {@code URL}
* @param attachment
* {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}, {@code true} by default.
* @param validFor
* The time at which the direct access {@code URL} will expire.
* @return A direct access {@code URL} object for the content.
*/
DirectAccessUrl requestContentDirectUrl(String archivedId, String renditionId, boolean attachment, Long validFor);

View File

@@ -31,6 +31,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.extensions.webscripts.servlet.FormData;
import org.alfresco.rest.api.model.AssocChild;
import org.alfresco.rest.api.model.AssocTarget;
import org.alfresco.rest.api.model.Document;
@@ -48,7 +50,6 @@ import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.springframework.extensions.webscripts.servlet.FormData;
/**
* File Folder (Nodes) API
@@ -63,34 +64,37 @@ public interface Nodes
/**
* Get the node representation for the given node.
*
* @param nodeId String
* @param nodeId
* String
* @return Node
*/
Node getNode(String nodeId);
/**
* Get the document representation for the given node.
*
* @param nodeRef NodeRef
* @param nodeRef
* NodeRef
* @return Document
*/
Document getDocument(NodeRef nodeRef);
/**
* Get the folder representation for the given node.
*
* @param nodeRef NodeRef
* @param nodeRef
* NodeRef
* @return Folder
*/
Folder getFolder(NodeRef nodeRef);
/**
* Get the folder or document representation (as appropriate) for the given node.
*
* @param nodeId String nodeId or well-known alias, eg. "-root-" or "-my-"
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* including:
* - incPrimaryParent
* @param nodeId
* String nodeId or well-known alias, eg. "-root-" or "-my-"
* @param parameters
* the {@link Parameters} object to get the parameters passed into the request including: - incPrimaryParent
* @return
*/
Node getFolderOrDocument(String nodeId, Parameters parameters);
@@ -102,7 +106,8 @@ public interface Nodes
/**
* Get the folder or document representation (as appropriate) for the given node.
*
* @param nodeRef A real Node
* @param nodeRef
* A real Node
* @param parentNodeRef
* @param nodeTypeQName
* @param includeParam
@@ -121,24 +126,25 @@ public interface Nodes
* @return
*/
List<Node> getFoldersOrDocuments(List<NodeRef> nodeRefs, List<String> includeParam, Map<String, UserInfo> mapUserInfo);
/**
* Get list of children of a parent folder.
*
* @param parentFolderNodeId String id of parent folder node or well-known alias, eg. "-root-" or "-my-"
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* including:
* - filter, sort & paging params (where, orderBy, skipCount, maxItems)
* - incFiles, incFolders (both true by default)
* @param parentFolderNodeId
* String id of parent folder node or well-known alias, eg. "-root-" or "-my-"
* @param parameters
* the {@link Parameters} object to get the parameters passed into the request including: - filter, sort & paging params (where, orderBy, skipCount, maxItems) - incFiles, incFolders (both true by default)
* @return a paged list of {@code org.alfresco.rest.api.model.Node} objects
*/
CollectionWithPagingInfo<Node> listChildren(String parentFolderNodeId, Parameters parameters);
/**
* Delete the given node. Note: will cascade delete for a folder.
*
* @param nodeId String id of node (folder or document)
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* - permanent (default false)
* @param nodeId
* String id of node (folder or document)
* @param parameters
* the {@link Parameters} object to get the parameters passed into the request - permanent (default false)
*/
void deleteNode(String nodeId, Parameters parameters);
@@ -178,7 +184,8 @@ public interface Nodes
*
* @param fileNodeId
* @param parameters
* @param recordActivity true, if an activity post is required.
* @param recordActivity
* true, if an activity post is required.
* @return
*/
BinaryResource getContent(String fileNodeId, Parameters parameters, boolean recordActivity);
@@ -186,9 +193,11 @@ public interface Nodes
/**
* Download file content.
*
* @param nodeRef the content nodeRef
* @param nodeRef
* the content nodeRef
* @param parameters
* @param recordActivity true, if an activity post is required.
* @param recordActivity
* true, if an activity post is required.
* @return
*/
BinaryResource getContent(NodeRef nodeRef, Parameters parameters, boolean recordActivity);
@@ -209,18 +218,24 @@ public interface Nodes
/**
* Uploads file content and meta-data into the repository.
*
* @param parentFolderNodeId String id of parent folder node or well-known alias, eg. "-root-" or "-my-"
* @param formData the {@link FormData}
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @param parentFolderNodeId
* String id of parent folder node or well-known alias, eg. "-root-" or "-my-"
* @param formData
* the {@link FormData}
* @param parameters
* the {@link Parameters} object to get the parameters passed into the request
* @return {@code Node} if successful
*/
Node upload(String parentFolderNodeId, FormData formData, Parameters parameters);
NodeRef validateNode(StoreRef storeRef, String nodeId);
List<NodeRef> validateNodes(StoreRef storeRef, List<String> nodeIds);
NodeRef validateNode(String nodeId);
NodeRef validateNode(NodeRef nodeRef);
NodeRef validateOrLookupNode(String nodeId, String path);
boolean nodeMatches(NodeRef nodeRef, Set<QName> expectedTypes, Set<QName> excludedTypes);
@@ -228,9 +243,12 @@ public interface Nodes
/**
* Determines whether the type of the given nodeRef is a sub-class of another class or not.
*
* @param nodeRef source nodeRef
* @param ofClassQName the class to test against
* @param validateNodeRef whether to validate the given source node or not
* @param nodeRef
* source nodeRef
* @param ofClassQName
* the class to test against
* @param validateNodeRef
* whether to validate the given source node or not
* @return true if the type of the given nodeRef is a sub-class of another class, otherwise false
*/
boolean isSubClass(NodeRef nodeRef, QName ofClassQName, boolean validateNodeRef);
@@ -238,7 +256,8 @@ public interface Nodes
/**
* Helper to create a QName from either a fully qualified or short-name QName string
*
* @param qnameStr Fully qualified or short-name QName string
* @param qnameStr
* Fully qualified or short-name QName string
* @return QName
*/
QName createQName(String qnameStr);
@@ -265,6 +284,7 @@ public interface Nodes
/**
* Lock a node
*
* @param nodeId
* @param lockInfo
* @param parameters
@@ -274,6 +294,7 @@ public interface Nodes
/**
* Unlock a node
*
* @param nodeId
* @param parameters
* @return
@@ -282,8 +303,11 @@ public interface Nodes
/**
* Gets a presigned URL to directly access content.
* @param nodeId The node id for which to obtain the direct access {@code URL}
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
*
* @param nodeId
* The node id for which to obtain the direct access {@code URL}
* @param attachment
* {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
* @return A direct access {@code URL} object for the content.
*/
default DirectAccessUrl requestContentDirectUrl(String nodeId, boolean attachment)
@@ -293,8 +317,11 @@ public interface Nodes
/**
* Gets a presigned URL to directly access content.
* @param nodeRef The node reference for which to obtain the direct access {@code URL}
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
*
* @param nodeRef
* The node reference for which to obtain the direct access {@code URL}
* @param attachment
* {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
* @return A direct access {@code URL} object for the content.
*/
default DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment)
@@ -304,9 +331,13 @@ public interface Nodes
/**
* Gets a presigned URL to directly access content.
* @param nodeId The node id for which to obtain the direct access {@code URL}
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
* @param validFor The time at which the direct access {@code URL} will expire.
*
* @param nodeId
* The node id for which to obtain the direct access {@code URL}
* @param attachment
* {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
* @param validFor
* The time at which the direct access {@code URL} will expire.
* @return A direct access {@code URL} object for the content.
*/
default DirectAccessUrl requestContentDirectUrl(String nodeId, boolean attachment, Long validFor)
@@ -316,17 +347,19 @@ public interface Nodes
/**
* Gets a presigned URL to directly access content.
* @param nodeRef The node reference for which to obtain the direct access {@code URL}
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
* @param validFor The time at which the direct access {@code URL} will expire.
*
* @param nodeRef
* The node reference for which to obtain the direct access {@code URL}
* @param attachment
* {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
* @param validFor
* The time at which the direct access {@code URL} will expire.
* @return A direct access {@code URL} object for the content.
*/
DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment, Long validFor);
/**
* Convert from node properties (map of QName to Serializable) retrieved from
* the respository to a map of String to Object that can be formatted/expressed
* as required by the API JSON response for get nodes, get person etc.
* Convert from node properties (map of QName to Serializable) retrieved from the respository to a map of String to Object that can be formatted/expressed as required by the API JSON response for get nodes, get person etc.
* <p>
* Returns null if there are no properties to return, rather than an empty map.
*
@@ -337,11 +370,10 @@ public interface Nodes
* @param excludedProps
* @return The map of properties, or null if none to return.
*/
Map<String, Object> mapFromNodeProperties(Map<QName, Serializable> nodeProps, List<String> selectParam, Map<String,UserInfo> mapUserInfo, List<String> excludedNS, List<QName> excludedProps);
Map<String, Object> mapFromNodeProperties(Map<QName, Serializable> nodeProps, List<String> selectParam, Map<String, UserInfo> mapUserInfo, List<String> excludedNS, List<QName> excludedProps);
/**
* Map from the JSON API format of properties (String to Object) to
* the typical node properties map used by the repository (QName to Serializable).
* Map from the JSON API format of properties (String to Object) to the typical node properties map used by the repository (QName to Serializable).
*
* @param props
* @return
@@ -351,25 +383,24 @@ public interface Nodes
/**
* Returns the path to the given nodeRef {@code nodeRefIn} or the archived nodeRef {@code archivedParentAssoc}.
*
* @param nodeRefIn the NodeRef
* @param archivedParentAssoc the ChildAssociationRef of the archived NodeRef
* @param nodeRefIn
* the NodeRef
* @param archivedParentAssoc
* the ChildAssociationRef of the archived NodeRef
* @return the path to the given node
*/
PathInfo lookupPathInfo(NodeRef nodeRefIn, ChildAssociationRef archivedParentAssoc);
/**
* Map from a String representation of aspect names to a set
* of QName objects, as used by the repository.
* Map from a String representation of aspect names to a set of QName objects, as used by the repository.
*
* @param aspectNames
* @return
*/
Set<QName> mapToNodeAspects(List<String> aspectNames);
/**
* Map from aspects (Set of QName) retrieved from the repository to a
* map List of String required that can be formatted/expressed as required
* by the API JSON response for get nodes, get person etc.
* Map from aspects (Set of QName) retrieved from the repository to a map List of String required that can be formatted/expressed as required by the API JSON response for get nodes, get person etc.
* <p>
* Returns null if there are no aspect names to return, rather than an empty list.
*
@@ -381,8 +412,7 @@ public interface Nodes
List<String> mapFromNodeAspects(Set<QName> nodeAspects, List<String> excludedNS, List<QName> excludedAspects);
/**
* Add aspects to the specified NodeRef. Aspects that appear in the exclusions list
* will be ignored.
* Add aspects to the specified NodeRef. Aspects that appear in the exclusions list will be ignored.
*
* @param nodeRef
* @param aspectNames
@@ -391,8 +421,7 @@ public interface Nodes
void addCustomAspects(NodeRef nodeRef, List<String> aspectNames, List<QName> exclusions);
/**
* Update aspects for the specified NodeRef. An empty list will result in
* aspects being <strong>removed</strong>.
* Update aspects for the specified NodeRef. An empty list will result in aspects being <strong>removed</strong>.
*
* @param nodeRef
* @param aspectNames
@@ -404,7 +433,6 @@ public interface Nodes
void validateProperties(Map<String, Object> properties, List<String> excludedNS, List<QName> excludedProperties);
/**
* API Constants - query parameters, etc
*/
@@ -456,4 +484,3 @@ public interface Nodes
String PARAM_ISPRIMARY = "isPrimary";
String PARAM_ASSOC_TYPE = "assocType";
}

View File

@@ -102,19 +102,21 @@ public class DeletedNodesImpl implements DeletedNodes, RecognizedParamsExtractor
/**
* Sets archived information on the Node
*
* @param aNode
* @param mapUserInfo
*/
private void mapArchiveInfo(Node aNode, Map<String, UserInfo> mapUserInfo)
{
if (mapUserInfo == null) {
if (mapUserInfo == null)
{
mapUserInfo = new HashMap<>();
}
Map<QName, Serializable> nodeProps = nodeService.getProperties(aNode.getNodeRef());
aNode.setArchivedAt((Date)nodeProps.get(ContentModel.PROP_ARCHIVED_DATE));
aNode.setArchivedByUser(aNode.lookupUserInfo((String)nodeProps.get(ContentModel.PROP_ARCHIVED_BY), mapUserInfo, personService));
aNode.setArchivedAt((Date) nodeProps.get(ContentModel.PROP_ARCHIVED_DATE));
aNode.setArchivedByUser(aNode.lookupUserInfo((String) nodeProps.get(ContentModel.PROP_ARCHIVED_BY), mapUserInfo, personService));
//Don't show parent id
// Don't show parent id
aNode.setParentId(null);
}
@@ -166,10 +168,10 @@ public class DeletedNodesImpl implements DeletedNodes, RecognizedParamsExtractor
List<Node> nodesFound = new ArrayList<Node>(result.getPage().size());
Map mapUserInfo = new HashMap<>();
for (NodeRef nRef:result.getPage())
for (NodeRef nRef : result.getPage())
{
Node foundNode = nodes.getFolderOrDocument(nRef, null, null, parameters.getInclude(), mapUserInfo);
mapArchiveInfo(foundNode,mapUserInfo);
mapArchiveInfo(foundNode, mapUserInfo);
nodesFound.add(foundNode);
}
@@ -179,10 +181,10 @@ public class DeletedNodesImpl implements DeletedNodes, RecognizedParamsExtractor
@Override
public Node getDeletedNode(String originalId, Parameters parameters, boolean fullnode, Map<String, UserInfo> mapUserInfo)
{
//First check the node is valid and has been archived.
// First check the node is valid and has been archived.
NodeRef validatedNodeRef = nodes.validateNode(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, originalId);
//Now get the Node
// Now get the Node
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, validatedNodeRef.getId());
NodeRef archivedNodeRef = nodeArchiveService.getArchivedNode(nodeRef);
@@ -196,10 +198,11 @@ public class DeletedNodesImpl implements DeletedNodes, RecognizedParamsExtractor
foundNode = nodes.getFolderOrDocument(archivedNodeRef, null, null, parameters.getInclude(), mapUserInfo);
}
if (foundNode != null) mapArchiveInfo(foundNode,null);
if (foundNode != null)
mapArchiveInfo(foundNode, null);
return foundNode;
}
@Override
public List<Node> getDeletedNodes(List<String> originalIds, Parameters parameters, boolean fullnode, Map<String, UserInfo> mapUserInfo)
{
@@ -234,7 +237,7 @@ public class DeletedNodesImpl implements DeletedNodes, RecognizedParamsExtractor
@Override
public Node restoreArchivedNode(String archivedId, NodeTargetAssoc nodeTargetAssoc)
{
//First check the node is valid and has been archived.
// First check the node is valid and has been archived.
NodeRef validatedNodeRef = nodes.validateNode(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, archivedId);
RestoreNodeReport restored = null;
@@ -263,16 +266,16 @@ public class DeletedNodesImpl implements DeletedNodes, RecognizedParamsExtractor
case FAILURE_INVALID_ARCHIVE_NODE:
throw new EntityNotFoundException(archivedId);
case FAILURE_INVALID_PARENT:
throw new NotFoundException("Invalid parent id "+restored.getTargetParentNodeRef());
throw new NotFoundException("Invalid parent id " + restored.getTargetParentNodeRef());
default:
throw new ApiException("Unable to restore node "+archivedId);
throw new ApiException("Unable to restore node " + archivedId);
}
}
@Override
public void purgeArchivedNode(String archivedId)
{
//First check the node is valid and has been archived.
// First check the node is valid and has been archived.
NodeRef validatedNodeRef = nodes.validateNode(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, archivedId);
nodeArchiveService.purgeArchivedNode(validatedNodeRef);
}
@@ -314,7 +317,7 @@ public class DeletedNodesImpl implements DeletedNodes, RecognizedParamsExtractor
@Override
public DirectAccessUrl requestContentDirectUrl(String originalNodeId, String renditionId, boolean attachment, Long validFor)
{
//First check the node is valid and has been archived.
// First check the node is valid and has been archived.
NodeRef validatedNodeRef = nodes.validateNode(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, originalNodeId);
if (renditionId != null)