Understanding Thought Processes
THOPs 1.0
THOPs 2.0
THOPs Examples
THOPs 1.0
Example 1: Hello World
Example 2: Hello Stranger
Example 3: Connections Query
Example 4: Connections Query with Built-Ins
THOPs 2.0
Example 1: Asynchronous Connections
Example 2: Long-Running Asynchronous Connections
Understanding Thought Processes
Saffron Technology™ Thought Processes (THOPs) are user-defined functions that allow you to tie together various SaffronMemoryBase™ and other capabilities using a scripting language. Thought Processes can help perform a wide variety of actions such as: simplifying the execution of complex sequential queries, calling outside applications to be used in conjunction with Saffron Technology reasoning functions, or converting query results into data suitable for UI widgets. THOPs are written in one of the supported script languages (currently, only JavaScript is available).
If you are familiar with other database products, think of Thought Processes as stored procedures. THOPs can be created via the REST API or by using the developer tools in Saffron Admin. After a Thought Process is defined, it becomes callable through the standard REST API.
Currently, SaffronMemoryBase includes two versions of Thought Processes: THOPs 1.0 and THOPs 2.0.
THOPs 1.0
These Thought Processes are run synchronously. THOPs 1.0 runs via an HTTP GET call with the calling client and waits for a result.
THOPs 1.0 are called using a GET operation and uses the Tomcat application server. This version is based on the Rhino JavaSript engine with extensions.
Use THOPs 1.0 for the following types of operations:
- Simple single queries
- Fast operations
- Where asynchronization is not necessary
THOPs 2.0
The THOPs 2.0 layer enables Thought Processes to run asynchronously and to have bi-directional communication with the calling client. The processes can be long-running, which is why we refer to them as Deep Thoughts (DT). When initiated by a caller, DTs can run arbitrarily long and can send messages to the client before delivering a final result.
The following is an example of a Deep Thought that can deliver a partial result to the calling client as soon as data is available and deliver the remaining results later:
Deep Thoughts can also listen to messages being sent by the caller and reply to those messages individually (as well as return partial or final results).
Here, THOPs can register for messages from the caller. THOPs can also reply to messages or send messages proactively:
Clients start an instance of a Deep Thought (DT) by POSTing to the thopname/result
resource. The result of the POST does not contain a final result, but an Event Bus address to listen for messages from the DT as well as a start address. Clients then send a message to the start address to actually start the DT (it lies dormant to give the client a chance to register to the listen address).
A DT can send a (partial) result to the client using the send(data)
function. data
is any JS object; it will be turned into JSON during transmission to the client.
A DT can register for messages sent by the client after the DT was started using the on(address, handler)
function. Address
is any string that the client uses to send messages to the DT. DT's handler
will then be called with the message as well as a replyHandler
. The DT can use the replyHandler
to send a message back to the calling client. (Alternatively, it can use the send
function.)
As part of default parameters for a DT, a timeout value can be specified after which a DT instance shuts down (and the client will be unable to send messages to the DT).
Use THOPs 2.0 for the following types of operations:
- Complex queries that cannot be expressed in a single query
- Business logic when writing apps based on SUIT and THOPS
- Integrating SaffronMemoryBase APIs and foreign APIs
- Using a stored procedure in a relational database
- Deploying code at run time
THOPs Examples
THOPs 1.0
Below is an example of the THOPs 1.0 layer. Note that this is primarily used for SaffronMemoryBase 10.x and runs only synchronous Thought Processes:
Example 1: Hello World
File: HelloWorld.js
function run() { return 'Hello World!'; }
Line 1: A THOP must have at least one function. In THOPs 1.0, the start function must be named run()
(The function is runAsync(…) for THOPs 2.0.)
Line 2: Return result. This can be a string or an object (which will be turned into JSON)
To run:
curl –u username ‘http://localhost:8080/ws/rest/proc/HelloWorld/result?access_key=demo’
Result format:
var data = { result: “Hello World”, debug: [{ … }], error: {…} or “” }
In order to turn the result into a JS object on the client side, run JSON.parse(data.result) !
Example 2: Hello Stranger
File: HelloStranger.js
function run(p) { debug (p); return 'Hello ' + p.name; }
Line 1: The first argument is a map of all parameters of the query part of the URL used in calling the THOP.
Line 2: Thought Processes include a built-in debug(…) function. Debug info is delivered as part of the result.
Line 3: The optional parameter p contains a map of key-value pairs that are specified when calling the Thought Process.
Example 3: Connections Query
File: connections.js
function run(p) { var smb = new Smb('http://mhtn01.saffrontech.org:8080/ws', '******', '12345'); var result = smb.connections('jira_all_saffron').q('project_key:suit').get(); return result.r.map(attr).value(); } function attr(r) { return r.a.c + ':' + r.a.v; }
Line 2: Create a connection to a SaffronMemoryBase cluster using an access_key (******) and secret (12345).
Line 3: Run a connections query on space jira_all_saffron with an AQL query project_key:suit. (The function q(...) is used to specify an AQL query.) The call to get() makes the actual call to the REST service. The resulting parsed JSON object is stored in the result variable.
Line 4: Use lo-dash to map the result to a list of category:values (see http://lodash.com/):
- result.r contains an array of attributes which are connected with project_key:suit in our example.
- Behind the scenes, the call to get() wraps this array as a lo-dash object and adds a map(...) function. Map calls the attr(...) function for each element in the array and builds a new array out of the return values.
- attr(...) is a function that turns an object like the one above into an AQL-compatible string and then returns it.
- The call to value() unwraps the object and returns the raw array.
Example 4: Connections Query with Built-Ins
File: connections-2.js
function run(p) { var result = connections('jira_all_saffron').q('project_key:suit').get(); return result.r.map(attr).value(); } function attr(r) { return r.a.c + ':' + r.a.v; }
Instead of creating your own SaffronMemoryBase connection object (as in Example 3), you can use the built-in connections that then connects to your local SaffronMemoryBase instance.
local.connections(…)
is connections(…)
THOPs 2.0
Below is an example of the THOPs 2.0 layer. This supports asynchronous Thought Processes:
Example 1: Asynchronous Connections
function runAsync(p, cb) { var smb = new Smb('http://mhtn01.saffrontech.org:8080/ws', 'demo', '12345'); var result = smb.connections('jira_all_saffron').q('project_key:suit').then( function (result) { debug(result); cb(result.r.map(attr).value()); } ); } function attr(r) { return r.a.c + ':' + r.a.v; }
Line 1: The first argument is a map of all parameters of the query part of the URL used in calling the THOP. In THOPs 2.0, the function is runAsync(…)
Line 2: Create a connection to a SaffronMemoryBase cluster using an access_key (******) and secret (12345).
Line 3: Run a connections query on space jira_all_saffron with an AQL query project_key:suit. (The function q(...) is used to specify an AQL query.) Instead of using GET when you create the connection, use then or async then.
Line 4: The result function is the callback (cb) handler. This function is called with the final result of the connection call at some point in the future.
Example 2: Long-Running Asynchronous Connections
function runAsync(p, cb) { on('marco', function(data){ send('polo'); }); on('quit', function(data){ cb('thanks!'); }); }
Some connections can run forever. They can also register events from the caller.
Line 2: The on function is used to register for an event that is private to this connection.
Line 3: The send function is used to send a message to the calling client.