To manage relationships (referred to as associations) between nodes use the NodesApi and the following methods:
Method | Description |
---|---|
listNodeChildren | List primary parent-child associations, see Listing Content of a Folder |
createSecondaryChildAssociation | Create secondary parent-child association |
deleteSecondaryChildAssociation | Delete secondary parent-child association |
listSecondaryChildren | List secondary parent-child associations |
createAssociation | Create peer-2-peer association |
deleteAssociation | Delete peer-2-peer association(s) |
listSourceAssociations | List source peer-2-peer associations |
listTargetAssociations | List target peer-2-peer associations |
For more information about this ReST API endpoint, see Working with Relationships Between Folders/Files.
For a description of the common parameters, such as include, see Common Parameters.
Assuming we have deployed the FDK content model (see Introduction to the FDK Content Model) with secondary parent-child and peer-2-peer association types, then the following code examples shows how to create those types of associations (it also shows how to upload files, create folder, create node of different type):
import org.alfresco.core.handler.NodesApi; import org.alfresco.core.model.*; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.File; import java.io.IOException; import java.util.*; @Component public class ManageAssociationsCmd { static final Logger LOGGER = LoggerFactory.getLogger(ManageAssociationsCmd.class); @Autowired NodesApi nodesApi; private Boolean autoRename = true; private Boolean majorVersion = true; private Boolean versioningEnabled = true; private String updateComment = null; private String updatedName = null; private List<String> include = null; private List<String> fields = null; private List<String> orderBy = null; private Integer skipCount = 0; private Integer maxItems = 100; private String where = null; private Boolean includeSource = false; public void execute() throws IOException { // List all folders and files (primary parent-child associations) in /Company Home/Data Dictionary NodeChildAssociationPagingList primaryChildAssociations = listPrimaryChildAssociations("-root-", "/Data Dictionary"); // Create gadget folder, gadget image, and gadget review Node gadgetFolderNode = createFolder("My Gadgets", ""); Node gadgetPictureNode = uploadFile(gadgetFolderNode.getId(), "gadget-picture.png", "somepicture.png"); Node gadgetReviewNode = uploadFile(gadgetFolderNode.getId(), "gadget-review.txt", "sometext.txt"); // Create the Gadget company node Map<String, Object> properties = new HashMap<>(); properties.put("fdk:email", "info@coolgadgets.com"); properties.put("fdk:url","www.coolgadgets.com"); properties.put("fdk:city","London"); Node companyNode = createNode(gadgetFolderNode.getId(), "Cool Gadgets Inc","fdk:company", properties); // Create a gadget node with associations using the FDK content model List<ChildAssociationBody> secondaryParentChildAssociations = new ArrayList<>(); ChildAssociationBody childAssoc = new ChildAssociationBody(); childAssoc.assocType("fdk:images"); childAssoc.setChildId(gadgetPictureNode.getId()); secondaryParentChildAssociations.add(childAssoc); List<AssociationBody> peer2peerAssociations = new ArrayList<>(); AssociationBody peer2peerAssoc = new AssociationBody(); peer2peerAssoc.assocType("fdk:reviews"); peer2peerAssoc.setTargetId(gadgetReviewNode.getId()); peer2peerAssociations.add(peer2peerAssoc); AssociationBody peer2peerAssoc2 = new AssociationBody(); peer2peerAssoc2.assocType("fdk:company"); peer2peerAssoc2.setTargetId(companyNode.getId()); peer2peerAssociations.add(peer2peerAssoc2); Node gadgetNode = createNodeWithAssociations( gadgetFolderNode.getId(),"My Gadget", "fdk:gadget", secondaryParentChildAssociations, peer2peerAssociations); // List secondary parent-child associations for a node NodeChildAssociationPagingList secondaryAssoc = listSecondaryChildAssociations(gadgetNode.getId()); // List peer-2-peer associations for a node NodeAssociationPagingList targetAssoc = listPeer2PeerAssociations(gadgetNode.getId()); } /** * List primary parent-child associations. Basically list folder contents. * * @param rootNodeId the id of the folder node that is the root. If relativeFolderPath is null, then content in this folder will be listed. Besides node ID the aliases -my-, -root- and -shared- are also supported. * @param relativeFolderPath path relative rootNodeId, if this is not null, then the content of this folder will be listed * @return a list of child node objects contained in the folder, or null if not found */ private NodeChildAssociationPagingList listPrimaryChildAssociations(String rootNodeId, String relativeFolderPath) { LOGGER.info("Listing primary child associations for folder {}{}", rootNodeId, relativeFolderPath); NodeChildAssociationPagingList result = nodesApi.listNodeChildren(rootNodeId, skipCount, maxItems, orderBy, where, include, relativeFolderPath, includeSource, fields).getBody().getList(); for (NodeChildAssociationEntry childNodeAssoc: result.getEntries()) { LOGGER.info("Found primary child [name=" + childNodeAssoc.getEntry().getName() + "]"); } return result; } /** * List secondary parent-child associations. * * @param nodeId the node to list assoc for * @return a list of child node objects contained in the node, or null if not found */ private NodeChildAssociationPagingList listSecondaryChildAssociations(String nodeId) { LOGGER.info("Listing secondary child associations for node {}", nodeId); NodeChildAssociationPagingList result = nodesApi.listSecondaryChildren( nodeId, where, include, skipCount, maxItems, includeSource, fields).getBody().getList(); for (NodeChildAssociationEntry childNodeAssoc: result.getEntries()) { LOGGER.info("Found secondary child [name=" + childNodeAssoc.getEntry().getName() + "]"); } return result; } /** * List peer-2-peer associations. * * @param nodeId the node to list assoc for * @return a list of assoc objects associated with the node */ private NodeAssociationPagingList listPeer2PeerAssociations(String nodeId) { LOGGER.info("Listing peer-2-peer associations for node {}", nodeId); NodeAssociationPagingList result = nodesApi.listTargetAssociations( nodeId, where, include, fields).getBody().getList(); for (NodeAssociationEntry targetAssoc: result.getEntries()) { LOGGER.info("Found target [name=" + targetAssoc.getEntry().getName() + "]"); } return result; } /** * Create a node with associations. * * @param parentNodeId the parent node id * @param nodeName the name of the node * @param nodeType the type of the node * @param secondaryParentChildAssociations a list of secondary parent-child associations that should be set up * @param peer2peerAssociations a list of peer-2-peer associations that should be set up * @return a node object for the newly created node, contains the ID, * such as e859588c-ae81-4c5e-a3b6-4c6109b6c905 */ private Node createNodeWithAssociations( String parentNodeId, String nodeName, String nodeType, List<ChildAssociationBody> secondaryParentChildAssociations, List<AssociationBody> peer2peerAssociations) { NodeBodyCreate nodeBodyCreate = new NodeBodyCreate(); nodeBodyCreate.setName(nodeName); nodeBodyCreate.setNodeType(nodeType); nodeBodyCreate.setSecondaryChildren(secondaryParentChildAssociations); nodeBodyCreate.setTargets(peer2peerAssociations); Node node = nodesApi.createNode(parentNodeId, nodeBodyCreate, autoRename, majorVersion, versioningEnabled, include, fields).getBody().getEntry(); LOGGER.info("Created new node with associations: {}", node); return node; } /** * Make the remote call to create a folder in the repository, if it does not exist. * * @param folderName the name of the folder * @param relativeFolderPath path relative to /Company Home * @return a node object for the newly created node, contains the ID, * such as e859588c-ae81-4c5e-a3b6-4c6109b6c905 */ private Node createFolder(String folderName, String relativeFolderPath) { String nodeId = "-root-"; NodeBodyCreate nodeBodyCreate = new NodeBodyCreate(); nodeBodyCreate.setName(folderName); nodeBodyCreate.setNodeType("cm:folder"); nodeBodyCreate.setRelativePath(relativeFolderPath); Node folderNode = nodesApi.createNode(nodeId, nodeBodyCreate, autoRename, majorVersion, versioningEnabled, include, fields).getBody().getEntry(); LOGGER.info("Created new folder: {}", folderNode); return folderNode; } /** * Create a node * * @param parentNodeId the node id for parent folder * @param nodeName the name of the node * @param nodeType the type of the node * @return a node object for the newly created node, contains the ID, * such as e859588c-ae81-4c5e-a3b6-4c6109b6c905 */ private Node createNode(String parentNodeId, String nodeName, String nodeType, Map<String, Object> properties) { NodeBodyCreate nodeBodyCreate = new NodeBodyCreate(); nodeBodyCreate.setName(nodeName); nodeBodyCreate.setNodeType(nodeType); nodeBodyCreate.setProperties(properties); Node node = nodesApi.createNode( parentNodeId, nodeBodyCreate, autoRename, majorVersion, versioningEnabled, include, fields).getBody().getEntry(); LOGGER.info("Created new node: {}", node); return node; } /** * Upload a file from disk */ private Node uploadFile(String folderId, String fileName, String filePath) { // Create the file node metadata NodeBodyCreate nodeBodyCreate = new NodeBodyCreate(); nodeBodyCreate.setName(fileName); nodeBodyCreate.setNodeType("cm:content"); Node fileNode = nodesApi.createNode( folderId, nodeBodyCreate, autoRename, majorVersion, versioningEnabled, include, fields).getBody().getEntry(); // Get the file bytes File someFile = new File(filePath); byte[] fileData = null; try { fileData = FileUtils.readFileToByteArray(someFile); } catch (IOException e) { e.printStackTrace(); } // Add the file node content Node updatedFileNode = nodesApi.updateNodeContent(fileNode.getId(), fileData, majorVersion, updateComment, updatedName, include, fields).getBody().getEntry(); LOGGER.info("Created file with content: {}", updatedFileNode.toString()); return updatedFileNode; } }
Executing the above code will result in logs such as follows:
% java -jar target/rest-api-0.0.1-SNAPSHOT.jar manage-associations 2021-04-30 16:26:22.678 INFO 22647 --- [ main] o.a.tutorial.restapi.RestApiApplication : Started RestApiApplication in 2.93 seconds (JVM running for 3.402) 2021-04-30 16:26:22.680 INFO 22647 --- [ main] o.a.tutorial.restapi.RestApiApplication : args[0]: manage-associations 2021-04-30 16:26:22.681 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Listing primary child associations for folder -root-/Data Dictionary 2021-04-30 16:26:23.071 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Email Templates] 2021-04-30 16:26:23.071 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Imap Configs] 2021-04-30 16:26:23.071 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Messages] 2021-04-30 16:26:23.071 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Models] 2021-04-30 16:26:23.071 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Node Templates] 2021-04-30 16:26:23.071 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Presentation Templates] 2021-04-30 16:26:23.071 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Rendering Actions Space] 2021-04-30 16:26:23.071 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Replication Actions Space] 2021-04-30 16:26:23.071 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=RSS Templates] 2021-04-30 16:26:23.071 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Saved Searches] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Scheduled Actions] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Scripts] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Smart Folder Downloads] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Smart Folder Templates] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Solr Facets Space] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Space Templates] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Transfers] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Web Client Extension] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Web Scripts] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Web Scripts Extensions] 2021-04-30 16:26:23.072 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found primary child [name=Workflow Definitions] 2021-04-30 16:26:23.184 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Created new folder: class Node { id: e6bacba5-0dba-40af-afa0-ff25e10a18bb name: My Gadgets nodeType: cm:folder isFolder: true isFile: false isLocked: false modifiedAt: 2021-04-30T15:26:23.129Z modifiedByUser: class UserInfo { displayName: Administrator id: admin } createdAt: 2021-04-30T15:26:23.129Z createdByUser: class UserInfo { displayName: Administrator id: admin } parentId: e439190c-3fe0-48a1-8a9a-374fbc54b570 isLink: null isFavorite: null content: null aspectNames: [cm:auditable] properties: null allowableOperations: null path: null permissions: null definition: null } 2021-04-30 16:26:23.482 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Created file with content: class Node { id: b9bf8f12-269f-46a3-97a8-16900644a7d6 name: gadget-picture.png nodeType: cm:content isFolder: false isFile: true isLocked: false modifiedAt: 2021-04-30T15:26:23.404Z modifiedByUser: class UserInfo { displayName: Administrator id: admin } createdAt: 2021-04-30T15:26:23.218Z createdByUser: class UserInfo { displayName: Administrator id: admin } parentId: e6bacba5-0dba-40af-afa0-ff25e10a18bb isLink: null isFavorite: null content: class ContentInfo { mimeType: image/png mimeTypeName: PNG Image sizeInBytes: 14799 encoding: UTF-8 } aspectNames: [cm:versionable, cm:auditable] properties: {cm:versionLabel=2.0, cm:versionType=MAJOR} allowableOperations: null path: null permissions: null definition: null } 2021-04-30 16:26:23.716 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Created file with content: class Node { id: bb35fdd6-f2f3-44e4-84c9-30e48efaf3d5 name: gadget-review.txt nodeType: cm:content isFolder: false isFile: true isLocked: false modifiedAt: 2021-04-30T15:26:23.644Z modifiedByUser: class UserInfo { displayName: Administrator id: admin } createdAt: 2021-04-30T15:26:23.507Z createdByUser: class UserInfo { displayName: Administrator id: admin } parentId: e6bacba5-0dba-40af-afa0-ff25e10a18bb isLink: null isFavorite: null content: class ContentInfo { mimeType: text/plain mimeTypeName: Plain Text sizeInBytes: 30 encoding: ISO-8859-1 } aspectNames: [cm:versionable, cm:auditable] properties: {cm:versionLabel=2.0, cm:versionType=MAJOR} allowableOperations: null path: null permissions: null definition: null } 2021-04-30 16:26:23.918 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Created new node: class Node { id: 01c5e298-a6c2-4b5c-81e0-195172626e22 name: Cool Gadgets Inc nodeType: fdk:company isFolder: false isFile: true isLocked: false modifiedAt: 2021-04-30T15:26:23.772Z modifiedByUser: class UserInfo { displayName: Administrator id: admin } createdAt: 2021-04-30T15:26:23.772Z createdByUser: class UserInfo { displayName: Administrator id: admin } parentId: e6bacba5-0dba-40af-afa0-ff25e10a18bb isLink: null isFavorite: null content: class ContentInfo { mimeType: application/octet-stream mimeTypeName: Binary File (Octet Stream) sizeInBytes: 0 encoding: UTF-8 } aspectNames: [cm:versionable, cm:auditable] properties: {fdk:email=info@coolgadgets.com, fdk:url=www.coolgadgets.com, cm:versionType=MAJOR, cm:versionLabel=1.0, fdk:city=London} allowableOperations: null path: null permissions: null definition: null } 2021-04-30 16:26:24.133 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Created new node with associations: class Node { id: c5f329e8-7872-4e92-abe1-e7dd5f5f48ba name: My Gadget nodeType: fdk:gadget isFolder: false isFile: true isLocked: false modifiedAt: 2021-04-30T15:26:23.950Z modifiedByUser: class UserInfo { displayName: Administrator id: admin } createdAt: 2021-04-30T15:26:23.950Z createdByUser: class UserInfo { displayName: Administrator id: admin } parentId: e6bacba5-0dba-40af-afa0-ff25e10a18bb isLink: null isFavorite: null content: class ContentInfo { mimeType: application/octet-stream mimeTypeName: Binary File (Octet Stream) sizeInBytes: 0 encoding: UTF-8 } aspectNames: [cm:versionable, cm:auditable] properties: {cm:versionLabel=1.0, cm:versionType=MAJOR} allowableOperations: null path: null permissions: null definition: null } 2021-04-30 16:26:24.134 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Listing secondary child associations for node c5f329e8-7872-4e92-abe1-e7dd5f5f48ba 2021-04-30 16:26:24.156 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found secondary child [name=gadget-picture.png] 2021-04-30 16:26:24.157 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Listing peer-2-peer associations for node c5f329e8-7872-4e92-abe1-e7dd5f5f48ba 2021-04-30 16:26:24.239 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found target [name=gadget-review.txt] 2021-04-30 16:26:24.239 INFO 22647 --- [ main] o.a.t.restapi.ManageAssociationsCmd : Found target [name=Cool Gadgets Inc]
To create associations for existing nodes use the createSecondaryChildAssociation and createAssociation methods.