Index: sparql.engine.inc =================================================================== RCS file: sparql.engine.inc diff -N sparql.engine.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sparql.engine.inc 15 May 2009 07:14:04 -0000 @@ -0,0 +1,161 @@ + + * @copyright Copyright (c) 2007-2008 Arto Bendiken. All rights reserved. + * @license GPL + * @package sparql.module + */ + +////////////////////////////////////////////////////////////////////////////// +// SPARQL query engine + +class SPARQL_Engine { + + static function execute($query) { + module_invoke_all('sparql', $query->type, $query); + + switch ($query->type) { + case 'ask': + return count(self::solutions($query->patterns)) > 0; + + case 'select': + $solutions = self::solutions($query->patterns); + + // DISTINCT/REDUCED + if ($query->distinct || $query->reduced) { + $solutions = array_unique($solutions); // FIXME + } + + // ORDER BY + if (!empty($query->order_by)) { + $order_keys = array_keys($query->order_by); + $order_columns = array(); + foreach ($solutions as $index => $solution) { + foreach ($order_keys as $var) { + $order_columns[$var][$index] = $solution[$var]; + } + } + $args = array(); + foreach ($order_keys as $order_key) { + $args[] = $order_columns[$order_key]; + $args[] = $query->order_by[$order_key] == 'asc' ? SORT_ASC : SORT_DESC; + } + $args[] =& $solutions; + call_user_func_array('array_multisort', $args); + } + + // OFFSET/LIMIT + if (!is_null($query->limit) || !is_null($query->offset)) { + $offset = !is_null($query->offset) ? $query->offset : 0; + $count = !is_null($query->limit) ? $query->limit : count($solutions) - $offset; + $solutions = array_slice($solutions, $offset, $count); + } + return $solutions; + + case 'construct': + // TODO; + return array(); + + case 'describe': + $subjects = !empty($query->uris) ? array_flip($query->uris) : array(); + foreach (self::solutions($query->patterns) as $solution) { + foreach ($query->vars as $var) { + if (!empty($solution[$var])) { + // FIXME: handle blank nodes + $subjects[(string)$solution[$var]] = TRUE; + } + } + } + $data = array(); + foreach (array_keys($subjects) as $subject) { + $data = array_merge($data, iterator_to_array(rdf_query($subject, NULL, NULL))); // TODO + } + return $data; + } + } + + static function solutions($patterns) { + $solutions = array(); + + foreach ($patterns as $counter => $pattern) { + //var_dump(sprintf("[%d] Matching triple pattern: %s:", $counter, implode(' ', $pattern))); + + @list($pattern, $options) = array_chunk($pattern, 3, TRUE); + $variables = array_filter($pattern, 'rdf_is_var'); + if (count($variables) == 0) { // The simple case + if (count(self::query(self::make_query($pattern))) == 0) { + $solutions = array(); + break; + } + } + else if (count($variables) == 3) { + $triples = self::query(self::make_query(NULL, NULL, NULL)); + $solutions = array_merge($solutions, self::make_bindings($variables, $triples)); + } + else if (count(self::get_touched_vars($variables, $solutions)) == 0) { + $triples = self::query(self::make_query($pattern)); + + if (empty($triples)) { + break; // TODO: check this + } + + $solutions = array_merge($solutions, self::make_bindings($variables, $triples)); + } + else { + foreach ($solutions as $index => &$bindings) { + $triples = self::query(self::make_query($pattern, $bindings)); + + if (empty($triples)) { + if (empty($options['optional'])) { + unset($solutions[$index]); // this solution failed + } + } + else { + $new_solutions = self::make_bindings($variables, $triples); + $bindings = array_merge($bindings, /*FIXME:*/ reset($new_solutions)); + } + } + } + } + + return $solutions; + } + + static function query($triple) { + return iterator_to_array(calL_user_func_array('rdf_query', $triple)); + } + + static function make_query($pattern, $bindings = array()) { + list($s, $p, $o) = $pattern; + return array( + !rdf_is_var($s) ? $s : (isset($bindings[$s->name]) ? $bindings[$s->name] : NULL), + !rdf_is_var($p) ? $p : (isset($bindings[$p->name]) ? $bindings[$p->name] : NULL), + !rdf_is_var($o) ? $o : (isset($bindings[$o->name]) ? $bindings[$o->name] : NULL), + ); + } + + static function make_bindings($vars, $matches) { + $output = array(); + foreach ($matches as $i => $match) { + foreach ($vars as $j => $var) { + $output[$i][$var->name] = $match[$j]; + } + } + return $output; + } + + static function get_touched_vars($vars, $bindings) { + $output = array(); + foreach ($bindings as $binding) { + foreach ($vars as $var) { + if (isset($binding[$var->name])) + $output[$var->name] = TRUE; + } + } + return $output; + } + +} // class SPARQL_Engine