Tree

Content that is organized into folders can be retrieved using the Tree API.

The Tree API lets you pull back an entire path-based folder and file structure of content within a single API call. The API call lets you specify a root node, a maximum depth to traverse down the path structure, paths that should be automatically expanded and query terms for filtering of root nodes.

The Tree API is deal to support a variety of cases including:

  • retrieval of multiple deeply-nested paths within a single call
  • support for a UI tree control that needs to refresh its state (both full and partial)
  • dynamic loading of specific nested-paths within a larger tree
  • filtering of content by path and query

Sample Data Structure

In order to demonstrate how the Tree API works, let's put in place an imaginary data set. Suppose we have the following content nodes:

  • { "title": "tyrion", "location": "dragonstone"}
  • { "title": "cersei", "location": "kingslanding"}
  • { "title": "jaime", "location": "kingslanding"}
  • { "title": "daenerys", "location": "dragonstone"}
  • { "title": "jon", "location": "winterfell"}
  • { "title": "arya", "location": "winterfell"}
  • { "title": "sansa", "location": "winterfell"}
  • { "title": "brandon", "location": "winterfell"}

And suppose these are organized into the following paths:

/shows/game-of-thrones/lannister/cersei
/shows/game-of-thrones/lannister/jaime
/shows/game-of-thrones/lannister/tyrion
/shows/game-of-thrones/stark/arya
/shows/game-of-thrones/stark/brandon
/shows/game-of-thrones/stark/sansa
/shows/game-of-thrones/targaryeon/daenerys
/shows/game-of-thrones/targaryeon/jon

Now let's see what we can do.

Tree API Request

The basic structure for a Tree API call is like this:

POST /repositories/{repositoryId}/branches/{branchId}/nodes/{nodeId}/tree

To execute the call, you must first identify the root node of the tree. Cloud CMS lets you work against at tree that starts at any root node that you wish. You can use the branch root / or you might use an offset of the branch root, such as /shows.

To identify the root node, you must supply:

  • repositoryId - the ID of the repository to work against
  • branchId - the ID of the branch to work against
  • nodeId - the ID of the node to work against (or use root to indicate the root node)

Thus, you could make a call like this:

POST /repositories/{repositoryId}/branches/{branchId}/nodes/root/tree

And pull back the entire tree - which essentially looks like this:

/
    shows/
        game-of-thrones/
            lannister/
                cersei
                jaime
                tyrion
            stark/
                arya
                brandon
                sansa
            targaryeon/
                daenerys
                jon

In addition, you may specify path as a request parameter to indicate an offset path from the root. For example, suppose you wanted to pull back a tree of everything under /shows/game-of-thrones. To do so, you can use the path parameter (just as with all the other Node API calls) to specify an offset from the root node - like this:

POST /repositories/{repositoryId}/branches/{branchId}/nodes/root/tree?path=/shows/game-of-thrones

And the results would essentially be:

game-of-thrones/
    lannister/
        cersei
        jaime
        tyrion
    stark/
        arya
        brandon
        sansa
    targaryeon/
        daenerys
        jon

Tree API Response

The exact API tree response is a JSON object. Depending on the options you set, it may come back with properties loaded, explicit paths expanded, depths traversed and so on. But more or less, for the query above, it may come back looking like this:

{
  "id" : "821c40ab613d9b5bcbbc656b62229301",
  "repositoryId" : "filefolder_tree_test2",
  "branchId" : "824da2339616ed245084",
  "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
  "container" : true,
  "filename" : "",
  "label" : "root",
  "path" : "/",
  "description" : "Repository Root",
  "typeQName" : "n:root",
  "qname" : "r:root",
  "properties" : {
    "type" : "object",
    "description" : "Repository Root"
  },
  "children" : [ {
    "id" : "78961600fea78bd0b25b",
    "repositoryId" : "filefolder_tree_test2",
    "branchId" : "824da2339616ed245084",
    "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
    "container" : true,
    "filename" : "shows",
    "label" : "shows",
    "path" : "/shows",
    "title" : "shows",
    "typeQName" : "n:node",
    "qname" : "o:78961600fea78bd0b25b",
    "properties" : {
      "title" : "shows"
    },
    "children" : [ {
      "id" : "a7d96e6722ee03e374e3",
      "repositoryId" : "filefolder_tree_test2",
      "branchId" : "824da2339616ed245084",
      "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
      "container" : true,
      "filename" : "game-of-thrones",
      "label" : "game-of-thrones",
      "path" : "/shows/game-of-thrones",
      "title" : "game-of-thrones",
      "typeQName" : "n:node",
      "qname" : "o:a7d96e6722ee03e374e3",
      "properties" : {
        "title" : "game-of-thrones"
      },
      "children" : [ {
        "id" : "29d93e724e535d3b2bf0",
        "repositoryId" : "filefolder_tree_test2",
        "branchId" : "824da2339616ed245084",
        "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
        "container" : true,
        "filename" : "stark",
        "label" : "stark",
        "path" : "/shows/game-of-thrones/stark",
        "title" : "stark",
        "typeQName" : "n:node",
        "qname" : "o:29d93e724e535d3b2bf0",
        "properties" : {
          "title" : "stark"
        },
        "children" : [ {
          "id" : "c7432422d859fedb901d",
          "repositoryId" : "filefolder_tree_test2",
          "branchId" : "824da2339616ed245084",
          "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
          "container" : false,
          "filename" : "sansa",
          "label" : "sansa",
          "path" : "/shows/game-of-thrones/stark/sansa",
          "title" : "sansa",
          "typeQName" : "n:node",
          "qname" : "o:c7432422d859fedb901d",
          "properties" : {
            "title" : "sansa",
            "location" : "winterfell"
          }
        }, {
          "id" : "b946b22c6e396111f9e3",
          "repositoryId" : "filefolder_tree_test2",
          "branchId" : "824da2339616ed245084",
          "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
          "container" : false,
          "filename" : "brandon",
          "label" : "brandon",
          "path" : "/shows/game-of-thrones/stark/brandon",
          "title" : "brandon",
          "typeQName" : "n:node",
          "qname" : "o:b946b22c6e396111f9e3",
          "properties" : {
            "title" : "brandon",
            "location" : "winterfell"
          }
        }, {
          "id" : "f124c3612b94f6717d6f",
          "repositoryId" : "filefolder_tree_test2",
          "branchId" : "824da2339616ed245084",
          "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
          "container" : false,
          "filename" : "arya",
          "label" : "arya",
          "path" : "/shows/game-of-thrones/stark/arya",
          "title" : "arya",
          "typeQName" : "n:node",
          "qname" : "o:f124c3612b94f6717d6f",
          "properties" : {
            "title" : "arya",
            "location" : "winterfell"
          }
        } ],
        "childCount" : 3
      }, {
        "id" : "dd7d8f72389419e2a6c8",
        "repositoryId" : "filefolder_tree_test2",
        "branchId" : "824da2339616ed245084",
        "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
        "container" : true,
        "filename" : "targaryeon",
        "label" : "targaryeon",
        "path" : "/shows/game-of-thrones/targaryeon",
        "title" : "targaryeon",
        "typeQName" : "n:node",
        "qname" : "o:dd7d8f72389419e2a6c8",
        "properties" : {
          "title" : "targaryeon"
        },
        "children" : [ {
          "id" : "874823083f56b7dd9e97",
          "repositoryId" : "filefolder_tree_test2",
          "branchId" : "824da2339616ed245084",
          "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
          "container" : false,
          "filename" : "daenerys",
          "label" : "daenerys",
          "path" : "/shows/game-of-thrones/targaryeon/daenerys",
          "title" : "daenerys",
          "typeQName" : "n:node",
          "qname" : "o:874823083f56b7dd9e97",
          "properties" : {
            "title" : "daenerys",
            "location" : "dragonstone"
          }
        }, {
          "id" : "cdc3da075c11be87f4d5",
          "repositoryId" : "filefolder_tree_test2",
          "branchId" : "824da2339616ed245084",
          "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
          "container" : false,
          "filename" : "jon",
          "label" : "jon",
          "path" : "/shows/game-of-thrones/targaryeon/jon",
          "title" : "jon",
          "typeQName" : "n:node",
          "qname" : "o:cdc3da075c11be87f4d5",
          "properties" : {
            "title" : "jon",
            "location" : "dragonstone"
          }
        } ],
        "childCount" : 2
      }, {
        "id" : "09847ca015714d0a8b2a",
        "repositoryId" : "filefolder_tree_test2",
        "branchId" : "824da2339616ed245084",
        "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
        "container" : true,
        "filename" : "lannister",
        "label" : "lannister",
        "path" : "/shows/game-of-thrones/lannister",
        "title" : "lannister",
        "typeQName" : "n:node",
        "qname" : "o:09847ca015714d0a8b2a",
        "properties" : {
          "title" : "lannister"
        },
        "children" : [ {
          "id" : "b6856cacd4be79c17353",
          "repositoryId" : "filefolder_tree_test2",
          "branchId" : "824da2339616ed245084",
          "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
          "container" : false,
          "filename" : "cersei",
          "label" : "cersei",
          "path" : "/shows/game-of-thrones/lannister/cersei",
          "title" : "cersei",
          "typeQName" : "n:node",
          "qname" : "o:b6856cacd4be79c17353",
          "properties" : {
            "title" : "cersei",
            "location" : "kingslanding"
          }
        }, {
          "id" : "108e36ee44ae48c0af23",
          "repositoryId" : "filefolder_tree_test2",
          "branchId" : "824da2339616ed245084",
          "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
          "container" : false,
          "filename" : "tyrion",
          "label" : "tyrion",
          "path" : "/shows/game-of-thrones/lannister/tyrion",
          "title" : "tyrion",
          "typeQName" : "n:node",
          "qname" : "o:108e36ee44ae48c0af23",
          "properties" : {
            "title" : "tyrion",
            "location" : "kingslanding"
          }
        }, {
          "id" : "22e412565b03876efded",
          "repositoryId" : "filefolder_tree_test2",
          "branchId" : "824da2339616ed245084",
          "rootNodeId" : "821c40ab613d9b5bcbbc656b62229301",
          "container" : false,
          "filename" : "jaime",
          "label" : "jaime",
          "path" : "/shows/game-of-thrones/lannister/jaime",
          "title" : "jaime",
          "typeQName" : "n:node",
          "qname" : "o:22e412565b03876efded",
          "properties" : {
            "title" : "jaime",
            "location" : "kingslanding"
          }
        } ],
        "childCount" : 3
      } ],
      "childCount" : 3
    } ],
    "childCount" : 1
  } ],
  "childCount" : 1
}

Which is a nested tree structure with children arrays holding the contained child elements. Each element in the tree structure is a summary of the node present that layer in the tree. Each element contains the following required properties:

  • repositoryId, branchId and id - identify the repository, branch and node of the underlying content item
  • rootNodeId - the ID of the root of the repository
  • path - the path relative to the root for the current element
  • container - whether the current element is a container (folder)
  • filename - the filename for the current element
  • label - a display friendly label for the current element

If the element is backed by a Node (meaning that the Tree API call had sufficient privileges to read the node backing the element, then the following will be populated as well:

  • title - the node title (if available)
  • description - the node description (if available)
  • typeQName - the QName of the definition backing the node
  • qname - the QName of the node

If the element is a container, it will also provide the following:

  • children - an array of its children
  • childCount - the number of children in the array

Retrieving Node Properties

If you specify properties=true in your Tree API request, each element in the tree will additionally have a properties object with all of the properties of any node-backend elements that were able to be read.

  • properties - properties map

You can specify properties=true like this:

POST /repositories/{repositoryId}/branches/{branchId}/nodes/root/tree?properties=true

Depth Limiting

You can limit the depth of your tree by using the depth request property.

  • A depth of 1 limits the results to only the immediate children
  • A depth of 2 limits the results to immediate children and grandchildren
  • An unspecified depth hands back the full tree (without limits)

You can pass depth to the Tree API like this:

POST /repositories/{repositoryId}/branches/{branchId}/nodes/root/tree?depth=3

Suppose we set depth=3. Then we would essentially get back this:

/
    shows/
        game-of-thrones/
            lannister/
            stark/
            targaryeon/

Containers-Only

At times, you may wish to only pull back a tree of folders and ignore or filter out any files. To do so, set the containers=true request parameter, like this:

POST /repositories/{repositoryId}/branches/{branchId}/nodes/root/tree?container=true

This will hand back a reduced tree consisting only of folders:

/
    shows/
        game-of-thrones/
            lannister/
            stark/
            targaryeon/

Query the Tree

You can query the tree results to filter down the result set to include only those members which match your query.

Suppose, for example, that you wanted to filter the tree to only include content that has location=dragonstone. To do so, you can pass a JSON object to the Tree API consisting of your query. The query is the same as any other query to Cloud CMS and conforms to the MongoDB query syntax.

POST /repositories/{repositoryId}/branches/{branchId}/nodes/root/tree

{
    "location": "dragonstone"
}

From a content perspective, this query matches the following:

  • { "title": "tyrion", "location": "dragonstone"}
  • { "title": "daenerys", "location": "dragonstone"}

Which corresponds to the paths:

/shows/game-of-thrones/lannister/tyrion
/shows/game-of-thrones/targaryeon/daenerys

And essentially, the following tree is produced:

/
    shows/
        game-of-thrones/
            lannister/
                tyrion
            targaryeon/
                daenerys

Note that the parent folders of any matching content are returned back. The parent structure back to the root node of your Tree API call will be returned so as to be consistent with everything else the Tree API does. It always returns things relative to the root node you specify.

That said, the elements for shows, game-of-thrones, lannister and targaryeon will be stripped to a minimum (as though there were not found). Only their core properties will be returned. The core properties consist of what is necessary so as to be useful for further manipulation or retrieval in additional calls.

Expand Paths

You may also want to have certain paths auto-expanded when the tree is loaded. You can do this by passing in one or more paths using the leaf request attribute.

Suppose, for example, that we want to load our example tree using a depth of 2 so that we only load up to the game-of-thrones folder. To set depth=2, we would do:

POST /repositories/{repositoryId}/branches/{branchId}/nodes/root/tree?depth=2

And we'd get:

/
    shows/
        game-of-thrones/

Suppose that's essentially right but we'd also like to expand the path down to Jon Snow. We can do it like this:

POST /repositories/{repositoryId}/branches/{branchId}/nodes/root/tree?depth=2&leaf=/shows/game-of-thrones/targaryeon/jon

And we'd get:

/
    shows/
        game-of-thrones/
            targaryeon/
                jon

We could also expand Cersei like this:

POST /repositories/{repositoryId}/branches/{branchId}/nodes/root/tree?depth=2&leaf=/shows/game-of-thrones/targaryeon/jon&leaf=/shows/game-of-thrones/lannister/cersei

And we'd get:

/
    shows/
        game-of-thrones/
            lannister
                cersei
            targaryeon/
                jon