Scripting Cookbook

Getting Started

To get started with Server-Side Scripting, please visit the Server Side Scripting page.

Code Samples

Here are some code samples of common data structures to help you get started.

Increment a Property

With this example, we want to keep track of a counter that tracks the number of updates made to a node. After a node
is created, if the user clicks update ten times, we want to have a counter on the node that indicates it has been
updated ten times.

This script should be bound to the p:beforeUpdateNode policy.

When a node is updated, this method will execute before the transaction commits to the database.
It gives you a chance to modify the node ahead of it being written.

Here, we check for the existence of the counter property and if it doesn't exist, we initialize it to 0.
We then increment the counter. The incremented value will be part of the node when it is saved.

function beforeUpdateNode(node)
{
    if (!node.data.counter) {
        node.data.counter = 0;
    }

    node.data.counter++;
}

Associate Category Values on Update

With this example, we imagine a scenario where we have an Article that can be assigned zero or more Categories. Each
category has a numeric value that we'd like to sum up before the Article saves so that the Article can store a total
sum of all categories.

Here is a schema we can imagine for Article (my:article):

{
    "type": "object",
    "_qname": "my:article",
    "properties": {
        "title": {
            "title": "Title",
            "type": "string"
        },
        "categoryKeys": {
            "title": "Category Keys",
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "totalCategoryValue": {
            "title": "Total Category Value",
            "type": "number",
            "readonly": true
        }
    }
}

Here is a schema we can imagine for Category (my:category):

{
    "type": "object",
    "_qname": "my:category",
    "properties": {
        "title": {
            "title": "Title",
            "type": "string"
        },
        "key": {
            "title": "Key",
            "type": "string"
        },
        "value": {
            "title": "Value",
            "type": "number"
        }
    }
}

We might have the following categories instantiated:

[{
    "title": "Brewers",
    "key": "mke",
    "value": 1.73
}, {
    "title": "Red Sox",
    "key": "bos",
    "value": 1.41
}, {
    "title": "Indians",
    "key": "cle",
    "value": 1.81
}]

And we might have an article being updated that looks like this:

{
    "title": "Future is hopeful for season opener faithfuls",
    "categoryKeys": ["mke", "cle"]
}

This script will sum up the value of each category identified by the Article and will populate the
totalCategoryValue property for the node.

function beforeUpdateNode(node)
{
    // reset to zero
    node.data.totalCategoryValue = 0;

    // if we have category keys, find the categories they describe and sum up values
    if (node.data.categoryKeys && node.data.categoryKeys.length > 0) {
    
        var query = {
            "_type": "my:category",
            "key": {
                "$in": node.data.categoryKeys
            }
        };
        
        var results = node.branch.query(query);
        for (var id in results) {
            var categoryValue = results[id].data.value;
            if (categoryValue) {
                node.data.totalCategoryValue += categoryValue;
            }
        }
    }
}

Associate Category Values on Update (using Relators)

This example builds on the previous one but uses relator properties to model out the categories. We encourage using
relators as they store graph level relationships between your objects for you.

For more information on relator properties, please check out our Relator Property documentation.

Building on the previous example, we might adjust the article to look like this:

{
    "type": "object",
    "_qname": "my:article",
    "properties": {
        "title": {
            "title": "Title",
            "type": "string"
        },
        "categories": {
            "title": "Categories",
            "type": "array",
            "items": {
                "type": "object"
            },
            "_relator": {
                "nodeType": "my:category"
            }
        },
        "totalCategoryValue": {
            "title": "Total Category Value",
            "type": "number",
            "readonly": true
        }
    }
}

When we create or update our Article, we can now use a picker to pick from Category (my:category) nodes.

The resulting JSON might look something like this:

{
    "title": Future is hopeful for season opener faithfuls",
    "categories": [
        {
            "id": "926c131bbb9814782bb0",
            "ref": "node://21a134c5ca2584abb9c8/144fae6eb52299319d43/33dc97ba1db9dd4799ad/926c131bbb9814782bb0",
            "title": "Brewers",
            "qname": "o:926c131bbb9814782bb0",
            "typeQName": "my:category"
        },
        {
            "id": "319d40fa695900c52db9",
            "ref": "node://21a134c5ca2584abb9c8/144fae6eb52299319d43/33dc97ba1db9dd4799ad/319d40fa695900c52db9",
            "title": "Indians",
            "qname": "o:319d40fa695900c52db9",
            "typeQName": "my:category"
        }
    ]
}

Where each entry in categories is a relator property structure.

Here is a modification to the script to work with categories as a multi-value relator property array.

function beforeUpdateNode(node)
{
    // reset to zero
    node.data.totalCategoryValue = 0;

    // if we have categories, find the related categories and sum up values
    if (node.data.categories && node.data.categories.length > 0) {
    
        var query = {
            "_doc": {
                "$in": []
            }
        };
        
        for (var i = 0; i < node.data.categories.length; i++) {
            query["_doc"]["$in"].push(node.data.categories[i].id);
        }                
        
        var results = node.branch.query(query);
        for (var id in results) {
            var categoryValue = results[id].data.value;
            if (categoryValue) {
                node.data.totalCategoryValue += categoryValue;
            }
        }
    }
}

Copy a property from a Parent Folder

With this example, we ensure that a node will copy a category property from a parent folder (in a folder hierarchy)
provided that the parent has a category on it.

This script can be bound to the p:beforeUpdateNode policy.

The node.parent property will provide a node reference to the parent. If there is no parent (meaning that the current
node does not have an a:child parental association pointing to it), then parent will be undefined. Be sure to check
that parent has a value as shown here.

function beforeUpdateNode(node)
{
    var parent = node.parent;
    if (parent && parent.data.category) {
        node.data.category = parent.data.category;
    }    
}