Functions

A Function is an executable unit of code that can take parameters and return a result. Using Functions you can perform Functional programming where logic and data are all together in a central place. Functions are similar to the Stored Procedures of RDBMS.

NOTE: This guide refers to the last available release of OrientDB. For past revisions look at Compatibility.

OrientDB Functions features:

  • are persistent
  • can be written in SQL or Javascript (Ruby, Scala, Java and other languages are coming)
  • can be executed via SQL, Java, REST and Studio
  • can call each other
  • supports recursion
  • have automatic mapping of parameters by position and name
  • plugins can inject new objects to being used by functions

Create your first function

To start using Functions the simplest way is using the Studio. Open the database and go to the “Functions” panel. Then write as name “sum”, add 2 parameters named “a” and “b” and now write the following code in the text area:

  1. return parseInt(a) + parseInt(b);

Click on the “Save” button. Your function has been saved and will appear on the left between the available functions.

Now let’s go to test it. On the bottom you will find 2 empty boxes. This is where you can insert the parameters when invoking the function. Write 3 and 5 as parameters and click “Execute” to see the result. “8.0” will appear in the output box below.

image

Why using parseInt() and not just a + b? because HTTP protocol passes parameters as strings.

Where are my functions saved?

Functions are saved in the database using the OFunction class and the following properties:

  • name, as the name of the function
  • code, as the code to execute
  • parameters, as an optional EMBEDDEDLIST of String containing the parameter names if any
  • idempotent, tells if the function is idempotent, namely if it changes the database. Read-only functions are idempotent. This is needed to avoid calling non-idempotent functions using the HTTP GET method

Concurrent editing

Since OrientDB uses 1 record per function, the MVCC mechanism is used to protect against concurrent record updates.

Usage

Usage via Java API

Using OrientDB’s functions from Java is straightforward. First get the reference to the Function Manager, get the right function and execute it passing the parameters (if any). In this example parameters are passed by position:

  1. ODatabaseDocumentTx db = new ODatabaseDocumentTx("local:/tmp/db");
  2. db.open("admin", "admin");
  3. OFunction sum = db.getMetadata().getFunctionLibrary().getFunction("sum");
  4. Number result = sum.execute(3, 5);

If you’re using the Blueprints Graph API get the reference to the Function in this way:

  1. OFunction sum = graph.getRawGraph().getMetadata().getFunctionLibrary().getFunction("sum");

You can execute functions passing parameters by name:

  1. Map<String,Object> params = new HashMap<String,Object>();
  2. params.put("a", 3);
  3. params.put("b", 5);
  4. Number result = sum.execute(params);

Usage via HTTP REST

Each function is exposed as a REST service allowing the receiving of parameters. Parameters can be passed by position in the URL, or starting from 2.1 can be passed in the request payload as JSON. In this case the mapping is not positional, but by name.

Example to execute the sum function created before passing 3 and 5 as parameters in the URL, so positional:

  1. http://localhost:2480/function/demo/sum/3/5

Since 2.1, parameters can be passed also in the request’s payload in a JSON, so by name:

  1. { "a": 3, "b": 5 }

Both calls will return an HTTP 202 OK with an envelope containing the result of the calculation:

  1. {"result":[{"@type":"d","@version":0,"value":2}]}

You can call with HTTP GET method only functions declared as “idempotent”. Use HTTP POST to call any functions.

If you’re executing the function using HTTP POST method, encode the content and set the HTTP request header to: "Content-Type: application/json".

For more information, see HTTP REST protocol. To learn how to write server-side function for web applications, see Server-Side functions.

Function return values in HTTP calls

When calling a function as a REST service, OrientDB encapsulates the result in a JSON and sends it to the client via HTTP. The result can be slightly different depending on the return value of the function. Here are some details about different cases:

  • a function that returns a number:
    1. return 31;

result:

  1. {"result":[{"@type":"d","@version":0,"value":31}]}
  • a function that returns a JS object
    1. return {"a":1, "b":"foo"}

result:

  1. {"result":[{"@type":"d","@version":0,"value":{"a":1,"b":"foo"}}]}
  • a function that returns an array
    1. return [1, 2, 3]

result:

  1. {"result":[{"@type":"d","@version":0,"value":[1,2,3]}]}
  • a function that returns a query result
    1. return db.query("select from OUser")

result:

  1. {
  2. "result": [
  3. {
  4. "@type": "d",
  5. "@rid": "#6:0",
  6. "@version": 1,
  7. "@class": "OUser",
  8. "name": "admin",
  9. "password": "...",
  10. "status": "ACTIVE",
  11. "roles": [
  12. "#4:0"
  13. ],
  14. "@fieldTypes": "roles=n"
  15. },
  16. {
  17. "@type": "d",
  18. "@rid": "#6:1",
  19. "@version": 1,
  20. "@class": "OUser",
  21. "name": "reader",
  22. "password": "...",
  23. "status": "ACTIVE",
  24. "roles": [
  25. "#4:1"
  26. ],
  27. "@fieldTypes": "roles=n"
  28. }
  29. ]
  30. }

Access to the databases from Functions

OrientDB always binds a special variable orient to use OrientDB services from inside the functions. The most important methods are:

Execute a query

Query is an idempotent command. To execute a query use the query() method. Example:

  1. return orient.getDatabase().query("select name from ouser");

Execute a query with external parameters

Create a new function with name getyUserRoles with the parameter user. Then write this code:

  1. return orient.getDatabase().query("select roles from ouser where name = ?", name );

The name parameter is bound as variable in Javascript. You can use this variable to build your query.

Execute a command

Commands can be written in any language supported by JVM. By default OrientDB supports “SQL” and “Javascript”.

SQL Command

  1. var gdb = orient.getGraph();
  2. var results = gdb.command( "sql", "select from Employee where company = ?", [ "Orient Technologies" ] );

The result of command is an array of objects, where objects can be:

  • OrientVertex instances if vertices are returned
  • OrientEdge instances if edges are returned
  • OIdentifiable, or any subclasses of it, instances if records are returned

Write your own repository classes

Functions are the perfect place to write the logic for your application to access to the database. You could adopt a DDD approach allowing the function to work as a Repository or a DAO.

This mechanism provides a thin (or thick if you prefer) layer of encapsulation which may protect you from database changes.

Furthermore each function is published and reachable via HTTP REST protocol allowing the automatic creation of a RESTful service.

Example

Below an example of functions to build a repository for OUser records.

function user_getAll(){

  1. return orient.getDatabase().query("select from ouser");

}

function user_getByName( name ){

  1. return orient.getDatabase().query("select from ouser where name = ?", name );

}

function user_getAdmin(){

  1. return user_getByName("admin");

}

function user_create( name, role ){

  1. var db = orient.getDatabase();
  2. var role = db.query("select from ORole where name = ?", roleName);
  3. if( role == null ){
  4. response.send(404, "Role name not found", "text/plain", "Error: role name not found" );
  5. } else {
  6. db.begin();
  7. try{
  8. var result = db.save({ "@class" : "OUser", name : "Luca", password : "Luc4", status: "ACTIVE", roles : role});
  9. db.commit();
  10. return result;
  11. }catch ( err ){
  12. db.rollback();
  13. response.send(500, "Error on creating new user", "text/plain", err.toString() );
  14. }
  15. }

}

image

Recursive calls

Create the new function with name “factorial” with the parameter “n”. Then write this code:

  1. if (num === 0)
  2. return 1;
  3. else
  4. return num * factorial( num - 1 );

image

This function calls itself to find the factorial number for <num> as parameter. The result is 3628800.0.

Server-Side functions

Server-Side functions can be used as Servlet replacement. To know how to call a Server-Side function, see Usage via HTTP REST. When server-side functions are called via HTTP REST protocol, OrientDB embeds a few additional variables:

  • request, as the HTTP request and implemented by OHttpRequestWrapper class
  • response, as the HTTP request response implemented by OHttpResponseWrapper class
  • util, as an utility class with helper functions to use inside the functions. It’s implemented by OFunctionUtilWrapper class

Request object

Refer to this object as “request”. Example:

  1. var params = request.getParameters();
Method signature Description Return type
getContent() Returns the request’s content String
getUser() Gets the request’s user name String
getContentType() Returns the request’s content type String
getHttpVersion() Return the request’s HTTP version String
getHttpMethod() Return the request’s HTTP method called String
getIfMatch() Return the request’s IF-MATCH header String
isMultipart() Returns if the requests has multipart boolean
getArguments() Returns the request’s arguments passed in REST form. Example: /2012/10/26 String[]
getArgument(<position>) Returns the request’s argument by position, or null if not found String
getParameters() Returns the request’s parameters String
getParameter(<name>) Returns the request’s parameter by name or null if not found String
hasParameters(<name>*) Returns the number of parameters found between those passed Integer
getSessionId() Returns the session-id String
getURL() Returns the request’s URL String

Response object

Refer to this object as “response”. Example:

  1. var db = orient.getDatabase();
  2. var roles = db.query("select from ORole where name = ?", roleName);
  3. if( roles == null || roles.length == 0 ){
  4. response.send(404, "Role name not found", "text/plain", "Error: role name not found" );
  5. } else {
  6. db.begin();
  7. try{
  8. var result = db.save({ "@class" : "OUser", name : "Luca", password : "Luc4", "roles" : roles});
  9. db.commit();
  10. return result;
  11. }catch ( err ){
  12. db.rollback();
  13. response.send(500, "Error on creating new user", "text/plain", err.toString() );
  14. }
  15. }
Method signature Description Return type
getHeader() Returns the response’s additional headers String
setHeader(String header) Sets the response’s additional headers to send back. To specify multiple headers use the line breaks Request object
getContentType() Returns the response’s content type. If null will be automatically detected String
setContentType(String contentType) Sets the response’s content type. If null will be automatically detected Request object
getCharacterSet() Returns the response’s character set used String
setCharacterSet(String characterSet) Sets the response’s character set Request object
getHttpVersion() String
writeStatus(int httpCode, String reason) Sets the response’s status as HTTP code and reason Request object
writeStatus(int httpCode, String reason) Sets the response’s status as HTTP code and reason Request object
writeHeaders(String contentType) Sets the response’s headers using the keep-alive Request object
writeHeaders(String contentType, boolean keepAlive) Sets the response’s headers specifying when using the keep-alive or not Request object
writeLine(String content) Writes a line in the response. A line feed will be appended at the end of the content Request object
writeContent(String content) Writes content directly to the response Request object
writeRecords(List<OIdentifiable> records) Writes records as response. The records are serialized in JSON format Request object
writeRecords( List<OIdentifiable> records, String fetchPlan) Writes records as response specifying a fetch-plan to serialize nested records. The records are serialized in JSON format Request object
writeRecord(ORecord record) Writes a record as response. The record is serialized in JSON format Request object
writeRecord(ORecord record, String fetchPlan) Writes a record as response. The record is serialized in JSON format Request object
send(int code, String reason, String contentType, Object content) Sends the complete HTTP response in one call Request object
send(int code, String reason, String contentType, Object content, String headers) Sends the complete HTTP response in one call specifying additional headers. Keep-alive is set Request object
send(int code, String reason, String contentType, Object content, String headers, boolean keepAlive) Sends the complete HTTP response in one call specifying additional headers Request object
sendStream(int code, String reason, String contentType, InputStream content, long size) Sends the complete HTTP response in one call specifying a stream as content Request object
flush() Flushes the content to the TCP/IP socket Request object

Util object

Refer to this object as util. Example:

  1. if( util.exists(year) ){
  2. print("\nYes, the year was passed!");
  3. }
Method signature Description Return type
exists(<variable>) Returns trues if any of the passed variables are defined. In JS, for example, a variable is defined if it’s not null and not equals to “undefined” Boolean

Native functions

OrientDB’s SQL dialect supports many functions written in native language. To obtain better performance you can write you own native functions in Java language and register them to the engine.

Compatibility

1.5.0 and before

OrientDB binds the following variables:

  • db, that is the current document database instance
  • gdb, that is the current graph database instance