Working with Relationships between Folders or Files - Alfresco Content Services - 23.4 - 23.4 - Ready - Alfresco - external

Alfresco Content Services

Platform
Alfresco
Product
Alfresco Content Services
Release
23.4
License

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):

Note: This code assumes the following two files exists in current directory: somepicture.png and sometext.txt.
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.