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.