Skip to content

Commit

Permalink
Custom functions can be registered using a full URI.
Browse files Browse the repository at this point in the history
  • Loading branch information
antoniogarrote committed Jun 29, 2016
1 parent baf797e commit 2ccc0d4
Show file tree
Hide file tree
Showing 7 changed files with 456 additions and 382 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,9 +455,10 @@ The store will try to be smart and not perform unnecessary evaluations of these
###Custom Filter Functions

Custom filter function can be registered into the store using the *registerCustomFunction* function. This function receives two argument, the name of the custom function and the associated implementation. This functions will be available in a SPARQL query using the prefix *custom*.
You can also use a full URI to identify the function that is going to be registered.
The function implementation will receive two arguments, an object linking to the store query filters engine and a list with the actual arguments. Arguments will consist of literal or URIs objects. Results from the function must also be literal or URI objects.

The query filters engine can be used to access auxiliary function to transform literals into JavaScript types using the *effectiveTypeValue* function, boolean values using the *effectiveBooleanValue*, to build boolean litearl objects (*ebvTrue*, *ebvFalse*) or return an error with the *ebvError*. Documentation and source code for the *QueryFilters* object n the 'js-query-engine' module can be consulted to find information about additional helper functions.
The query filters engine can be used to access auxiliary function to transform literals into JavaScript types using the *effectiveTypeValue* function, boolean values using the *effectiveBooleanValue*, to build boolean literal objects (*ebvTrue*, *ebvFalse*) or return an error with the *ebvError*. Documentation and source code for the *QueryFilters* object n the 'js-query-engine' module can be consulted to find information about additional helper functions.

The following test shows a simple examples of how custom functions can be invoked:

Expand All @@ -473,8 +474,9 @@ new Store({name:'test', overwrite:true}, function(err,store) {
function(err) {

var invoked = false;
// instead of 'my_addition_check' a full URI can be used 'http://test.com/my_fns/my_addition_check'
store.registerCustomFunction('my_addition_check', function(engine,args) {
// equivalent to var v1 = parseInt(args[0].value), v2 = parseInt(args[1]);
// equivalent to var v1 = parseInt(args[0].value), v2 = parseInt(args[1].value);

var v1 = engine.effectiveTypeValue(args[0]);
var v2 = engine.effectiveTypeValue(args[1]);
Expand Down
11 changes: 8 additions & 3 deletions dist/rdfstore.js
Original file line number Diff line number Diff line change
Expand Up @@ -19426,7 +19426,7 @@ Store.prototype.close = function(cb) {
/**
* Version of the store
*/
Store.VERSION = "0.9.12";
Store.VERSION = "0.9.13";

/**
* Create a new RDFStore instance that will be
Expand Down Expand Up @@ -22817,7 +22817,9 @@ module.exports = (function() {
regex.expressionType = 'regex';
regex.text = e1;
regex.pattern = e2;
regex.flags = eo[2];
if(eo != null) {
regex.flags = eo[2];
}

return regex;
},
Expand Down Expand Up @@ -28446,7 +28448,7 @@ QueryFilters.runIriRefOrFunction = function(iriref, args, bindings,queryEngine,
} else {
var ops = [];
for(var i=0; i<args.length; i++) {
ops.push(QueryFilters.runFilter(args[i], bindings, queryEngine, dataset, env))
ops.push(QueryFilters.runFilter(args[i], bindings, queryEngine, dataset, env));
}

var fun = Utils.lexicalFormBaseUri(iriref, env);
Expand Down Expand Up @@ -28696,7 +28698,10 @@ QueryFilters.runIriRefOrFunction = function(iriref, args, bindings,queryEngine,
} else {
return QueryFilters.ebvError();
}
} else if(queryEngine.customFns[fun] != null) {
return queryEngine.customFns[fun](QueryFilters, ops);
} else {

// unknown function
return QueryFilters.ebvError();
}
Expand Down
748 changes: 374 additions & 374 deletions dist/rdfstore_min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rdfstore",
"version": "0.9.12",
"version": "0.9.13",
"description": "RDF graph store supporting the SPARQL query language",
"keywords": [
"RDF",
Expand Down
64 changes: 64 additions & 0 deletions spec/store_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,39 @@ describe("Store", function () {

});

it("Should pass integration test RegisterCustomFunction with a URI for the function", function (done) {
new Store.Store({name: 'test', overwrite: true}, function (err, store) {
store.load(
'text/n3',
'@prefix test: <http://test.com/> .\
test:A test:prop 5.\
test:B test:prop 4.\
test:C test:prop 1.\
test:D test:prop 3.',
function (err) {

var invoked = false;
store.registerCustomFunction('http://test.com/my_addition', function (engine, args) {
var v1 = engine.effectiveTypeValue(args[0]);
var v2 = engine.effectiveTypeValue(args[1]);
return engine.ebvBoolean(v1 + v2 < 5);
});
store.execute(
'PREFIX test: <http://test.com/> SELECT * { ?x test:prop ?v1 . ?y test:prop ?v2 . filter(<http://test.com/my_addition>(?v1,?v2)) }',
function (err, results) {
expect(results.length).toBe(3);
for (var i = 0; i < results.length; i++) {
expect(parseInt(results[i].v1.value) + parseInt(results[i].v2.value) < 5);
}
done();
}
);

});
});

});

it("Should be able to use GRAPH variables", function (done) {
new Store.Store({name: 'test', overwrite: true}, function (err, store) {
store.load(
Expand Down Expand Up @@ -1251,6 +1284,37 @@ describe("Store", function () {
});
});
});

it("Should process numeric filters", function(done){
var query="PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\
SELECT ?s { \
?s ?p ?o .\
FILTER (xsd:double(?o) > \"10.0\"^^<http://www.w3.org/2001/XMLSchema#double>)\
}";

new Store.Store({name: 'test', overwrite: true}, function (err, store) {
expect(err).toBe(null);
store.execute('INSERT DATA { <a1> <b> 16.0 . \
<a2> <b> 3.0 . \
<a3> <d> "11.4"^^<http://www.w3.org/2001/XMLSchema#double> . \
<a4> <d> "0.3"^^<http://www.w3.org/2001/XMLSchema#double> . \
<a5> <c> 13 .\
<a6> <c> "3"^^<http://www.w3.org/2001/XMLSchema#integer> }', function(){
store.execute(query, function(err, results) {
expect(err).toBe(null);
expect(results.length).toBe(3);
var vars = {};
for(var i=0; i<results.length; i++) {
vars[results[i]['s'].value] = true;
}
expect(vars['a1']).toBe(true);
expect(vars['a3']).toBe(true);
expect(vars['a5']).toBe(true);
done();
});
});
});
});
});

/*
Expand Down
5 changes: 4 additions & 1 deletion src/query_filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -1493,7 +1493,7 @@ QueryFilters.runIriRefOrFunction = function(iriref, args, bindings,queryEngine,
} else {
var ops = [];
for(var i=0; i<args.length; i++) {
ops.push(QueryFilters.runFilter(args[i], bindings, queryEngine, dataset, env))
ops.push(QueryFilters.runFilter(args[i], bindings, queryEngine, dataset, env));
}

var fun = Utils.lexicalFormBaseUri(iriref, env);
Expand Down Expand Up @@ -1743,7 +1743,10 @@ QueryFilters.runIriRefOrFunction = function(iriref, args, bindings,queryEngine,
} else {
return QueryFilters.ebvError();
}
} else if(queryEngine.customFns[fun] != null) {
return queryEngine.customFns[fun](QueryFilters, ops);
} else {

// unknown function
return QueryFilters.ebvError();
}
Expand Down
2 changes: 1 addition & 1 deletion src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,7 @@ Store.prototype.close = function(cb) {
/**
* Version of the store
*/
Store.VERSION = "0.9.12";
Store.VERSION = "0.9.13";

/**
* Create a new RDFStore instance that will be
Expand Down

0 comments on commit 2ccc0d4

Please sign in to comment.