Source for file RdqlMemEngine.php

Documentation is available at RdqlMemEngine.php

  1. <?php
  2.  
  3. // ----------------------------------------------------------------------------------
  4. // Class: RdqlMemEngine
  5. // ----------------------------------------------------------------------------------
  6.  
  7. /**
  8. * This class performes as RDQL query on a MemModel.
  9. *
  10. * Provided an rdql query parsed into an array of php variables and constraints
  11. * at first the engine searches for tuples matching all patterns from the WHERE clause
  12. * of the given RDQL query. Then the query result set is filtered with evaluated
  13. * boolean expressions from the AND clause of the given RDQL query.
  14. *
  15. *
  16. * <BR><BR>History:<UL>
  17. * <LI>09-08-2004 : findTriplesMatchingPattern() rewritten to only use the $model->find()
  18. * function for accessing the model. Changed the method of comparing literals to literals->equals
  19. * according to actual specification
  20. * <LI>05-12-2004 : Bug in the handling of empty Literals fixed.</LI>
  21. * <LI>03-17-2004 : Function findTuplesMatchingOnePattern()
  22. * pass-by-reference bug fixed</LI>
  23. * <LI>08-29-2003 : Function filterTuples(): some bugs fixed:
  24. * - strEqExpr with NE operator
  25. * - regExExpr combined with other expr. and negation (!)
  26. * e.g. !(?x ~~ "/sth/" && ?x > 5)
  27. * <LI>07-27-2003 : First version of this class</LI>
  28. *
  29. * @version V0.9.3
  30. * @author Radoslaw Oldakowski <radol@gmx.de>
  31. *
  32. * @package rdql
  33. * @access public
  34. */
  35.  
  36. Class RdqlMemEngine extends RdqlEngine {
  37.  
  38.  
  39. /**
  40. * Parsed query variables and constraints.
  41. *
  42. * @var array [''selectVars''][] = ?VARNAME
  43. * [''sources''][] = URI
  44. * [''patterns''][][''subject''][''value''] = VARorURI
  45. * [''predicate''][''value''] = VARorURI
  46. * [''object''][''value''] = VARorURIorLiterl
  47. * [''is_literal''] = boolean
  48. * [''l_lang''] = string
  49. * [''l_dtype''] = string
  50. * [''filters''][][''string''] = string
  51. * [''evalFilterStr''] = string
  52. * [''reqexEqExprs''][][''var''] = ?VARNAME
  53. * [''operator''] = (eq | ne)
  54. * [''regex''] = string
  55. * [''strEqExprs''][][''var''] = ?VARNAME
  56. * [''operator''] = (eq | ne)
  57. * [''value''] = string
  58. * [''value_type''] = (''variable'' | ''URI'' | ''Literal'')
  59. * [''value_lang''] = string
  60. * [''value_dtype''] = string
  61. * [''numExpr''][''vars''][] = ?VARNAME
  62. * ( [] stands for an integer index - 0..N )
  63. * @access private
  64. */
  65. var $parsedQuery;
  66.  
  67.  
  68. /**
  69. * Perform an RDQL Query on the given MemModel.
  70. *
  71. * @param object MemModel &$memModel
  72. * @param array &$parsedQuery (the same format as $this->parsedQuery)
  73. * @param boolean $returnNodes
  74. * @return array [][?VARNAME] = object Node (if $returnNodes = TRUE)
  75. * OR array [][?VARNAME] = string
  76. *
  77. * @access public
  78. */
  79. function & queryModel(&$memModel, &$parsedQuery, $returnNodes = TRUE) {
  80.  
  81. $this->parsedQuery = $parsedQuery;
  82.  
  83. // find tuples matching all patterns
  84. $res = $this->findTuplesMatchingAllPatterns($memModel);
  85.  
  86. // filter tuples
  87. if (isset($parsedQuery[''filters'']))
  88. $res = $this->filterTuples($res);
  89.  
  90. // select variables to be returned
  91. $res = $this->selectVariables($res);
  92.  
  93. if(!$returnNodes)
  94. return $this->toString($res);
  95.  
  96. return $res;
  97. }
  98.  
  99. /**
  100. * Find triples matching all patterns of an RDQL query and return an array
  101. * with variables from all patterns and their corresponding values.
  102. * The variable values returned are instances of object Node.
  103. *
  104. * @param object MemModel &$memModel
  105. * @return array [][?VARNAME] = object Node
  106. *
  107. * @access private
  108. */
  109. function findTuplesMatchingAllPatterns(&$memModel) {
  110.  
  111. $resultSet = $this->findTuplesMatchingOnePattern($memModel, $this->parsedQuery[''patterns''][0]);
  112. for ($i=1; $i<count($this->parsedQuery[''patterns'']); $i++) {
  113. $rs = $this->findTuplesMatchingOnePattern($memModel, $this->parsedQuery[''patterns''][$i]);
  114. $resultSet = $this->joinTuples($resultSet, $rs);
  115. }
  116. return $resultSet;
  117. }
  118.  
  119.  
  120. /**
  121. * Find tuples matching one pattern and return an array with pattern
  122. * variables and their corresponding values (instances of object Node).
  123. *
  124. * @param object MemModel &$memModel
  125. * @param array &$pattern [''subject''][''value''] = VARorURI
  126. * [''predicate''][''value''] = VARorURI
  127. * [''object''][''value''] = VARorURIorLiterl
  128. * [''is_literal''] = boolean
  129. * [''l_lang''] = string
  130. * [''l_dtype''] = string
  131. * @return array [][?VARNAME] = object Node
  132. *
  133. * @access private
  134. */
  135. function findTuplesMatchingOnePattern(&$memModel, &$pattern) {
  136.  
  137. $resultSet = array();
  138. $i = 0;
  139. // parameters to be passed to the method findTriplesMatchingPattern
  140. foreach ($pattern as $key => $v) {
  141. if ($v[''value''] && $v[''value'']{0} == ''?'') {
  142. if ($key == ''object'') {
  143. $param[''object''][''is_a''] = ''ANY'';
  144. $param[''object''][''string''] = ''ANY'';
  145. $param[''object''][''lang''] = NULL;
  146. $param[''object''][''dtype''] = NULL;
  147. } else
  148. $param[$key] = ''ANY'';
  149. $var[$i][''key''] = $key;
  150. $var[$i++][''val''] = $v[''value''];
  151. }else
  152. if (isset($v[''is_literal''])) {
  153. $param[$key][''is_a''] = ''Literal'';
  154. $param[$key][''string''] = $v[''value''];
  155. $param[$key][''lang''] = $v[''l_lang''];
  156. $param[$key][''dtype''] = $v[''l_dtype''];
  157. }else{
  158. if ($key == ''object'') {
  159. $param[$key][''is_a''] = ''Resource'';
  160. $param[$key][''string''] = $v[''value''];
  161. $param[$key][''lang''] = NULL;
  162. $param[$key][''dtype''] = NULL;
  163. }else
  164. $param[$key] = $v[''value''];
  165. }
  166. }
  167. // find pattern internal bindings e.g. (?x, ?z, ?x)
  168. $intBindings = NULL;
  169. for ($i=0; $i<count($var); $i++)
  170. foreach($var as $n => $v)
  171. if ($i != $n && $var[$i][''val''] == $v[''val''])
  172. $intBindings[] = $var[$i][''key''];
  173.  
  174. // find triples of the $memModel matching $pattern
  175. $resModel = $this->findTriplesMatchingPattern($memModel, $param[''subject''],
  176. $param[''predicate''],
  177. $param[''object''][''is_a''],
  178. $param[''object''][''string''],
  179. $param[''object''][''lang''],
  180. $param[''object''][''dtype''],
  181. $intBindings);
  182.  
  183. // set values of the pattern variables to be returned
  184. if ($pattern[''subject''][''value'']{0} == ''?'') {
  185. $n = 0;
  186. foreach ($resModel->triples as $triple)
  187. $resultSet[$n++][$pattern[''subject''][''value'']] = $triple->subj;
  188. }
  189. if ($pattern[''predicate''][''value'']{0} == ''?'') {
  190. $n = 0;
  191. foreach ($resModel->triples as $triple)
  192. $resultSet[$n++][$pattern[''predicate''][''value'']] = $triple->pred;
  193. }
  194. if ($pattern[''object''][''value''] && $pattern[''object''][''value'']{0} == ''?'') {
  195. $n = 0;
  196. foreach ($resModel->triples as $triple)
  197. $resultSet[$n++][$pattern[''object''][''value'']] = $triple->obj;
  198. }
  199. return $resultSet;
  200. }
  201.  
  202.  
  203. /**
  204. * Search in $memModel for triples matching one pattern from the WHERE clause.
  205. * ''ANY'' input for $subjLabel..$objLabel, $obj_is will match anything.
  206. * NULL input for $objDtype will only match obj->dtype = NULL
  207. * NULL input for $objLanguage will match obj->lang = NULL or anything if a
  208. * literal is datatyped (except for XMLLiterals and plain literals)
  209. * This method also checks internal bindings if provided.
  210. *
  211. * @param object MemModel $memModel
  212. * @param string $subjLabel
  213. * @param string $predLabel
  214. * @param string $objLabel
  215. * @param string $obj_is
  216. * @param string $objLanguage
  217. * @param string $objDtype
  218. * @param array $intBindings [] = string
  219. * @return object MemModel
  220. * @access private
  221. */
  222. function findTriplesMatchingPattern(&$memModel, $subjLabel, $predLabel, $obj_is,
  223. $objLabel, $objLang, $objDtype, &$intBindings) {
  224.  
  225. $res = new MemModel();
  226.  
  227. if($memModel->isEmpty())
  228. return $res;
  229.  
  230. if ($subjLabel==''ANY'')
  231. {
  232. $subj=NULL;
  233. } else
  234. {
  235. $subj=new Resource($subjLabel);
  236. };
  237. if ($predLabel==''ANY'')
  238. {
  239. $pred=NULL;
  240. } else
  241. {
  242. $pred=new Resource($predLabel);
  243. };
  244. if ($objLabel==''ANY'')
  245. {
  246. $obj=NULL;
  247. } else
  248. {
  249. if ($obj_is == ''Literal'')
  250. {
  251. $obj=new Literal($objLabel);
  252. $obj->setDatatype($objDtype);
  253. $obj->setLanguage($objLang);
  254. } else {
  255. $obj=new Resource($objLabel);
  256. }
  257. };
  258. $res=$memModel->find($subj,$pred,$obj);
  259.  
  260. if ($intBindings)
  261. foreach ($res->triples as $triple)
  262. {
  263. if (!$this->_checkIntBindings($triple, $intBindings))
  264. {
  265. $res->remove($triple);
  266. }
  267. }
  268.  
  269. return $res;
  270. }
  271.  
  272. /**
  273. * Perform an SQL-like inner join on two resultSets.
  274. *
  275. * @param array &$finalRes [][?VARNAME] = object Node
  276. * @param array &$res [][?VARNAME] = object Node
  277. * @return array [][?VARNAME] = object Node
  278. *
  279. * @access private
  280. */
  281. function joinTuples(&$finalRes, &$res) {
  282.  
  283. if (count($finalRes) == 0 || count($res) == 0)
  284. return array();
  285.  
  286. // find joint variables and new variables to be added to $finalRes
  287. $jointVars = array();
  288. $newVars = array();
  289. foreach ($res[0] as $varname => $node) {
  290. if (array_key_exists($varname, $finalRes[0]))
  291. $jointVars[] = $varname;
  292. else
  293. $newVars[] = $varname;
  294. }
  295.  
  296. // eliminate rows of $finalRes in which the values of $jointVars do not have
  297. // a corresponding row in $res.
  298. foreach ($finalRes as $n => $fRes) {
  299. foreach ($res as $i => $r) {
  300. $ok = TRUE;
  301. foreach ($jointVars as $j_varname)
  302. if ($r[$j_varname] != $fRes[$j_varname]) {
  303. $ok = FALSE;
  304. break;
  305. }
  306. if ($ok)
  307. break;
  308. }
  309. if (!$ok)
  310. unset($finalRes[$n]);
  311. }
  312.  
  313. // join $res and $finalRes
  314. $joinedRes = array();
  315. foreach ($res as $i => $r) {
  316. foreach ($finalRes as $n => $fRes) {
  317. $ok = TRUE;
  318. foreach ($jointVars as $j_varname)
  319. if ($r[$j_varname] != $fRes[$j_varname]) {
  320. $ok = FALSE;
  321. break;
  322. }
  323. if ($ok) {
  324. $joinedRow = $finalRes[$n];
  325. foreach($newVars as $n_varname)
  326. $joinedRow[$n_varname] = $r[$n_varname];
  327. $joinedRes[] = $joinedRow;
  328. }
  329. }
  330. }
  331. return $joinedRes;
  332. }
  333.  
  334. /**
  335. * Filter the result-set of query variables by evaluating each filter from the
  336. * AND clause of the RDQL query.
  337. *
  338. * @param array &$finalRes [][?VARNAME] = object Node
  339. * @return array [][?VARNAME] = object Node
  340. * @access private
  341. */
  342. function filterTuples(&$finalRes) {
  343.  
  344. foreach ($this->parsedQuery[''filters''] as $filter) {
  345.  
  346. foreach ($finalRes as $n => $fRes) {
  347. $evalFilterStr = $filter[''evalFilterStr''];
  348.  
  349. // evaluate regex equality expressions of each filter
  350. foreach ($filter[''regexEqExprs''] as $i => $expr) {
  351.  
  352. preg_match($expr[''regex''], $fRes[$expr[''var'']]->getLabel(), $match);
  353. $op = substr($expr[''operator''], 0,1);
  354. if (($op != ''!'' && !isset($match[0])) || ($op == ''!'' && isset($match[0])))
  355. $evalFilterStr = str_replace("##RegEx_$i##", ''FALSE'', $evalFilterStr);
  356. else
  357. $evalFilterStr = str_replace("##RegEx_$i##", ''TRUE'', $evalFilterStr);
  358.  
  359. }
  360.  
  361. // evaluate string equality expressions
  362. foreach ($filter[''strEqExprs''] as $i => $expr) {
  363.  
  364. $exprBoolVal = ''FALSE'';
  365. switch ($expr[''value_type'']) {
  366.  
  367. case ''variable'':
  368. if (($fRes[$expr[''var'']] == $fRes[$expr[''value'']] && $expr[''operator''] == ''eq'') ||
  369. ($fRes[$expr[''var'']] != $fRes[$expr[''value'']] && $expr[''operator''] == ''ne''))
  370. $exprBoolVal = ''TRUE'';
  371. break;
  372. case ''URI'':
  373.  
  374. if (is_a($fRes[$expr[''var'']], ''Literal'')) {
  375. if ($expr[''operator''] == ''ne'')
  376. $exprBoolVal = ''TRUE'';
  377. break;
  378. }
  379.  
  380. if (($fRes[$expr[''var'']]->getLabel() == $expr[''value''] && $expr[''operator''] == ''eq'') ||
  381. ($fRes[$expr[''var'']]->getLabel() != $expr[''value''] && $expr[''operator''] == ''ne''))
  382. $exprBoolVal = ''TRUE'';
  383. break;
  384. case ''Literal'':
  385.  
  386. if (!is_a($fRes[$expr[''var'']], ''Literal'')) {
  387. if ($expr[''operator''] == ''ne'')
  388. $exprBoolVal = ''TRUE'';
  389. break;
  390. }
  391.  
  392. $filterLiteral= new Literal($expr[''value''],$expr[''value_lang'']);
  393. $filterLiteral->setDatatype($expr[''value_dtype'']);
  394. $equal=$fRes[$expr[''var'']]->equals($filterLiteral);
  395. /* if ($fRes[$expr[''var'']]->getLabel() == $expr[''value''] &&
  396. $fRes[$expr[''var'']]->getDatatype() == $expr[''value_dtype'']) {
  397. $equal = TRUE;
  398. // Lang tags only differentiate literals in rdf:XMLLiterals and plain literals.
  399. // Therefore if a literal is datatyped ignore the language tag.
  400. if ((($expr[''value_dtype''] == NULL) ||
  401. ($expr[''value_dtype''] == ''http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral'') ||
  402. ($expr[''value_dtype''] == ''http://www.w3.org/2001/XMLSchema#string'')) &&
  403. (($fRes[$expr[''var'']]->getLanguage() != $expr[''value_lang''])))
  404. $equal = FALSE;
  405. }else
  406. $equal = FALSE;
  407. */
  408. if (($equal && $expr[''operator''] == ''eq'') ||
  409. (!$equal && $expr[''operator''] == ''ne''))
  410. $exprBoolVal = ''TRUE'';
  411. else
  412. $exprBoolVal = ''FALSE'';
  413.  
  414. }
  415. $evalFilterStr = str_replace("##strEqExpr_$i##", $exprBoolVal, $evalFilterStr);
  416. }
  417.  
  418. // evaluate numerical expressions
  419. foreach ($filter[''numExprVars''] as $varName) {
  420. $varValue = "''" .$fRes[$varName]->getLabel() ."''";
  421. $evalFilterStr = str_replace($varName, $varValue, $evalFilterStr);
  422. }
  423.  
  424. eval("\$filterBoolVal = $evalFilterStr; \$eval_filter_ok = TRUE;");
  425. if (!isset($eval_filter_ok))
  426. trigger_error(RDQL_AND_ERR ."''" .htmlspecialchars($filter[''string'']) ."''", E_USER_ERROR);
  427.  
  428. if (!$filterBoolVal)
  429. unset($finalRes[$n]);
  430. }
  431. }
  432.  
  433. return $finalRes;
  434. }
  435.  
  436.  
  437. /**
  438. * Remove all conditional variables from the result-set and leave only variables
  439. * specified in the SELECT clause of the RDQL query.
  440. *
  441. * @param array &$finalRes [][?VARNAME] = object Node
  442. * @return array [][?VARNAME] = object Node
  443. * @access private
  444. */
  445. function selectVariables(&$finalRes) {
  446.  
  447. // if nothing has been found return only one row of $finalRes
  448. // with select variables having empty values
  449. if (count($finalRes) == 0) {
  450. foreach ($this->parsedQuery[''selectVars''] as $selectVar)
  451. $finalRes[0][$selectVar] = NULL;
  452. return $finalRes;
  453. }
  454. // return only selectVars in the same order as given in the RDQL query
  455. // and reindex $finalRes.
  456. $n = 0;
  457. foreach($finalRes as $key => $val) {
  458. foreach ($this->parsedQuery[''selectVars''] as $selectVar)
  459. $resultSet[$n][$selectVar] = $val[$selectVar];
  460. unset($finalRes[$key]);
  461. ++$n;
  462. }
  463.  
  464. return $resultSet;
  465. }
  466.  
  467.  
  468. /**
  469. * Convert the variable values of $finalRes from objects to their string serialization.
  470. *
  471. * @param array &$finalRes [][?VARNAME] = object Node
  472. * @return array [][?VARNAME] = string
  473. * @access private
  474. */
  475. function toString(&$finalRes) {
  476.  
  477. foreach ($finalRes as $n => $tuple)
  478. foreach ($tuple as $varname => $node) {
  479. if (is_a($node, ''Resource''))
  480. $res[$n][$varname] = ''<'' .$node->getLabel() .''>'';
  481. elseif (is_a($node, ''Literal'')) {
  482. $res[$n][$varname] = ''"'' .$node->getLabel() .''"'';
  483. if ($node->getLanguage())
  484. $res[$n][$varname] .= '' (xml:lang="'' .$node->getLanguage() .''")'';
  485. if ($node->getDatatype())
  486. $res[$n][$varname] .= '' (rdf:datatype="'' .$node->getDatatype() .''")'';
  487. }else
  488. $res[$n][$varname] = $node;
  489. }
  490. return $res;
  491. }
  492.  
  493.  
  494. /**
  495. * Check if the given triple meets pattern internal bindings
  496. * e.g. (?x, ?z, ?x) ==> statement subject must be identical with the statement object
  497. *
  498. * @param object statement &$triple
  499. * @param array &$intBindings [] = string
  500. * @return boolean
  501. * @access private
  502. */
  503. function _checkIntBindings (&$triple, &$intBindings) {
  504.  
  505. if (in_array(''subject'', $intBindings)) {
  506. if (in_array(''predicate'', $intBindings))
  507. if ($triple->subj != $triple->pred)
  508. return FALSE;
  509. if (in_array(''object'', $intBindings)) {
  510. if (is_a($triple->obj, ''Literal''))
  511. return FALSE;
  512. elseif ($triple->subj != $triple->obj)
  513. return FALSE;
  514. }
  515. return TRUE;
  516. }
  517. if (in_array(''predicate'', $intBindings)) {
  518. if (is_a($triple->obj, ''Literal''))
  519. return FALSE;
  520. elseif ($triple->pred != $triple->obj)
  521. return FALSE;
  522. return TRUE;
  523. }
  524. }
  525.  
  526.  
  527. /**
  528. * Check if the lang and dtype of the passed object Literal are equal $lang and $dtype
  529. * !!! Language only differentiates literals in rdf:XMLLiterals and plain literals (xsd:string).
  530. * !!! Therefore if a literal is datatyped ignore the language.
  531. *
  532. * @param object Literal $literal
  533. * @param string $dtype1
  534. * @param string $dtype2
  535. * @return boolean
  536. * @access private
  537. */
  538. function _equalsLangDtype ($literal, $lang, $dtype) {
  539.  
  540. if ($dtype == $literal->getDatatype()) {
  541. if (($dtype == NULL ||
  542. $dtype == ''http://www.w3.org/2001/XMLSchema#string'' ||
  543. $dtype == ''http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral'') &&
  544. ($lang != $literal->getLanguage()))
  545. return FALSE;
  546. return TRUE;
  547. }
  548. return FALSE;
  549. }
  550. } // end: Class RdqlMemEngine
  551.  
  552. ?>

Documentation generated on Fri, 13 Jan 2006 07:49:16 +0100 by phpDocumentor 1.3.0RC4