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:
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:
return a + 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.
Functions are saved in the database using the OFunction class and the following properties:
Since OrientDB uses 1 record per function, the MVCC mechanism is used to protect against concurrent record updates.
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:
ODatabaseDocumentTx db = new ODatabaseDocumentTx("local:/tmp/db");
db.open("admin", "admin");
OFunction sum = db.getMetadata().getFunctionLibrary().getFunction("sum");
Number result = sum.execute(3, 5);
If you're using the Blueprints Graph API get the reference to the Function in this way:
OFunction sum = graph.getRawGraph().getMetadata().getFunctionLibrary().getFunction("sum");
You can execute functions passing parameters by name:
Map<String,Object> params = new HashMap<String,Object>();
params.put("a", 3);
params.put("b", 5);
Number result = sum.execute(params);
Each function is exposed as a REST service allowing the receiving of parameters. parameters are passed by position.
Below how to execute the "sum" function created before:
http://localhost:2480/function/demo/sum/3/5
This will return an HTTP 202 OK with an envelope containing the result of the calculation:
{"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.
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:
return 31;
result:
{"result":[{"@type":"d","@version":0,"value":31}]}
return {"a":1, "b":"foo"}
result:
{"result":[{"@type":"d","@version":0,"value":{"a":1,"b":"foo"}}]}
return [1, 2, 3]
result:
{"result":[{"@type":"d","@version":0,"value":[1,2,3]}]}
return db.query("select from OUser")
result:
{
"result": [
{
"@type": "d",
"@rid": "#6:0",
"@version": 1,
"@class": "OUser",
"name": "admin",
"password": "...",
"status": "ACTIVE",
"roles": [
"#4:0"
],
"@fieldTypes": "roles=n"
},
{
"@type": "d",
"@rid": "#6:1",
"@version": 1,
"@class": "OUser",
"name": "reader",
"password": "...",
"status": "ACTIVE",
"roles": [
"#4:1"
],
"@fieldTypes": "roles=n"
}
]
}
OrientDB always binds a special variable "orient" to use OrientDB services from inside the functions. The most important methods are:
Query is an idempotent command. To execute a query use the query()
method. Example:
return orient.getDatabase().query("select name from ouser");
Create a new function with name "getyUserRoles" with the parameter "user". Then write this code:
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.
Commands can be written in any language supported by JVM. By default OrientDB supports "SQL" and "Javascript".
var gdb = orient.getGraph();
var results = gdb.command( "sql", "select from Employee where company = ?", [ "Orient Technologies" ] );
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.
Below an example of functions to build a repository for OUser records.
function user_getAll(){
return orient.getDatabase().query("select from ouser");
}
function user_getByName( name ){
return orient.getDatabase().query("select from ouser where name = ?", name );
}
function user_getAdmin(){
return user_getByName("admin");
}
function user_create( name, role ){
var db = orient.getDatabase();
var role = db.query("select from ORole where name = ?", roleName);
if( role == null ){
response.send(404, "Role name not found", "text/plain", "Error: role name not found" );
} else {
db.begin();
try{
var result = db.save({ "@class" : "OUser", name : "Luca", password : "Luc4", roles : role});
db.commit();
return result;
}catch ( err ){
db.rollback();
response.send(500, "Error on creating new user", "text/plain", err.toString() );
}
}
}
Create the new function with name "factorial" with the parameter "n". Then write this code:
if (num === 0)
return 1;
else
return num * factorial( num - 1 );
This function calls itself to find the factorial number for <num>
as parameter. The result is 3628800.0
.
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:
OHttpRequestWrapper
classOHttpResponseWrapper
classOFunctionUtilWrapper
classRefer to this object as "request". Example:
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 |
Refer to this object as "response". Example:
var db = orient.getDatabase();
var roles = db.query("select from ORole where name = ?", roleName);
if( roles == null || roles.length == 0 ){
response.send(404, "Role name not found", "text/plain", "Error: role name not found" );
} else {
db.begin();
try{
var result = db.save({ "@class" : "OUser", name : "Luca", password : "Luc4", "roles" : roles});
db.commit();
return result;
}catch ( err ){
db.rollback();
response.send(500, "Error on creating new user", "text/plain", err.toString() );
}
}
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 |
Refer to this object as "util". Example:
if( util.exists(year) ){
print("\nYes, the year was passed!");
}
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 |
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.
OrientDB binds the following variables: