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

Documentation generated on Fri, 1 Jun 2007 16:51:08 +0200 by phpDocumentor 1.3.2