📄 expr.inc.php
字号:
if ($this->isDBonly() || $this->isTextOnly())
{
$this->clearPoint();
$point = 'point';
}
}
}
if ($point == 'point')
{
if ($this->isDBandText())
{
$point = 'merge';
$left->setPoint('point');
$right->setPoint('point');
}
}
if (is_null($point) && !DefaultOpCollection::isBoolean($op))
{
$point = 'point';
}
$this->setPoint($point);
}
private function isDBonly()
{
return $this->getHasDb() && !$this->getHasText();
}
private function isTextOnly()
{
return !$this->getHasDb() && $this->getHasText();
}
private function isDBandText()
{
return $this->getHasDb() && $this->getHasText();
}
public function appliesToContext()
{
return $this->left()->appliesToContext() | $this->right()->appliesToContext();
}
/**
* Enter description here...
*
* @param OpExpr $expr
*/
protected function clearPoint()
{
if (DefaultOpCollection::isBoolean($this))
{
$this->left()->clearPoint();
$this->right()->clearPoint();
}
if ($this->isMergePoint())
{
$this->setPoint(null);
}
}
protected function isMergePoint()
{
return in_array($this->getPoint(), array('merge','point'));
}
/**
* Returns the operator on the expression
*
* @return ExprOp
*/
public function op()
{
return $this->op;
}
/**
* Returns true if the negative of the operator should be used in evaluation
*
* @param boolean $not
* @return boolean
*/
public function not($not=null)
{
if (!is_null($not))
{
$this->not = $not;
}
return $this->not;
}
/**
* The left side of the expression
*
* @return Expr
*/
public function &left()
{
return $this->left_expr;
}
/**
* The right side of the expression
*
* @return Expr
*/
public function &right()
{
return $this->right_expr;
}
/**
* Converts the expression to a string
*
* @return string
*/
public function __toString()
{
// _kt may not translate well here.
$expr = $this->left_expr . ' ' . _kt($this->op) .' ' . $this->right_expr;
if (is_null($this->parent))
{
if ($this->not())
{
$expr = _kt('NOT') . $expr;
}
return $expr;
}
if ($this->parent->isOpExpr())
{
if ($this->parent->op != $this->op && in_array($this->op, DefaultOpCollection::$boolean))
{
$expr = "($expr)";
}
}
if ($this->not())
{
$expr = "!($expr)";
}
return $expr;
}
/**
* Is the expression valid
*
* @return boolean
*/
public function is_valid()
{
$left = $this->left();
$right = $this->right();
return $left->is_valid() && $right->is_valid();
}
/**
* Finds the results that are in both record sets.
*
* @param array $leftres
* @param array $rightres
* @return array
*/
protected static function _intersect($leftres, $rightres)
{
if (empty($leftres) || empty($rightres))
{
return array(); // small optimisation
}
$result = array();
foreach($leftres as $item)
{
$document_id = $item->Id;
if (!$item->IsLive)
{
continue;
}
if (array_key_exists($document_id, $rightres))
{
$check = $rightres[$document_id];
$result[$document_id] = ($item->Rank < $check->Rank)?$check:$item;
}
}
return $result;
}
protected static function intersect($leftres, $rightres)
{
return array(
'docs'=>self::_intersect($leftres['docs'],$rightres['docs']),
'folders'=>self::_intersect($leftres['folders'],$rightres['folders'])
);
}
protected static function union($leftres, $rightres)
{
return array(
'docs'=>self::_union($leftres['docs'],$rightres['docs']),
'folders'=>self::_union($leftres['folders'],$rightres['folders'])
);
}
/**
* The objective of this function is to merge the results so that there is a union of the results,
* but there should be no duplicates.
*
* @param array $leftres
* @param array $rightres
* @return array
*/
protected static function _union($leftres, $rightres)
{
if (empty($leftres))
{
return $rightres; // small optimisation
}
if (empty($rightres))
{
return $leftres; // small optimisation
}
$result = array();
foreach($leftres as $item)
{
if ($item->IsLive)
{
$result[$item->Id] = $item;
}
}
foreach($rightres as $item)
{
if (!array_key_exists($item->Id, $result) || $item->Rank > $result[$item->Id]->Rank)
{
$result[$item->Id] = $item;
}
}
return $result;
}
/**
* Enter description here...
*
* @param OpExpr $left
* @param ExprOp $op
* @param OpExpr $right
* @param boolean $not
*/
public function transform(& $left, & $op, & $right, & $not)
{
if (!$left->isOpExpr() || !$right->isOpExpr() || !DefaultOpCollection::isBoolean($op))
{
return;
}
if ($left->isTextOnly() && $right->isDBonly())
{
// we just swap the items around, to ease other transformations
$tmp = $left;
$left = $right;
$right = $tmp;
return;
}
if ($op != $right->op() || !DefaultOpCollection::isBoolean($right))
{
return;
}
if ($op == ExprOp::OP_OR && ($not || $right->not()))
{
// NOTE: we can't transform. e.g.
// db or !(db or txt) => db or !db and !txt
// so nothing to do
// BUT: db and !(db and txt) => db and !db and !txt
return;
}
$rightLeft = $right->left();
$rightRight = $right->right();
if ($left->isDBonly() && $rightLeft->isDBonly())
{
$newLeft = new OpExpr( $left, $op, $rightLeft );
$right = $rightRight;
$left = $newLeft;
return;
}
if ($left->isTextOnly() && $rightRight->isTextOnly())
{
$newRight = new OpExpr($left, $op, $rightRight);
$left = $rightLeft;
$right = $newRight;
return;
}
}
private function findDBNode($start, $op, $what)
{
if ($start->op() != $op)
{
return null;
}
switch($what)
{
case 'db':
if ($start->isDBonly())
{
return $start;
}
break;
case 'txt':
if ($start->isTextOnly())
{
return $start;
}
break;
}
$node = $this->findDBNode($start->left(), $op, $what);
if (is_null($left))
{
$node = $this->findDBNode($start->right(), $op, $what);
}
return $node;
}
public function traverse($object, $method, $param)
{
if ($this->isOpExpr())
{
$object->$method($param);
}
}
private function exploreItem($item, & $group, $interest)
{
if (($interest == 'db' && $item->getHasDb()) ||
($interest == 'text' && $item->getHasText()))
{
if (in_array($item->op(), array(ExprOp::OP_OR, ExprOp::OP_AND)))
{
$this->exploreItem($item->left(), $group, $interest);
$this->exploreItem($item->right(), $group, $interest);
}
else
{
$group[] = $item;
}
}
}
private function explore($left, $right, & $group, $interest)
{
$this->exploreItem($left, $group, $interest);
$this->exploreItem($right, $group, $interest);
}
private function exec_db_query($op, $group)
{
if (empty($group)) { return array(); }
$exprbuilder = new SQLQueryBuilder($this->getContext());
if (count($group) == 1)
{
$sql = $exprbuilder->buildComplexQuery($group[0]);
}
else
{
$sql = $exprbuilder->buildSimpleQuery($op, $group);
}
if (empty($sql))
{
return array();
}
$results = array();
global $default;
$default->log->debug("SEARCH SQL: $sql");
$rs = DBUtil::getResultArray($sql);
if (PEAR::isError($rs))
{
throw new Exception($rs->getMessage());
}
foreach($rs as $item)
{
$id = $item['id'];
$rank = $exprbuilder->getRanking($item);
if (!array_key_exists($id, $results) || $rank > $results[$id]->Rank)
{
if ($this->context == ExprContext::DOCUMENT)
{
$results[$id] = new DocumentResultItem($id, $rank, $item['title'], $exprbuilder->getResultText($item));
}
else
{
$results[$id] = new FolderResultItem($id, $rank, $item['title'], $exprbuilder->getResultText($item));
}
}
}
return $results;
}
private function exec_text_query($op, $group)
{
if (($this->getContext() != ExprContext::DOCUMENT) || empty($group))
{
return array();
}
$exprbuilder = new TextQueryBuilder();
if (count($group) == 1)
{
$query = $exprbuilder->buildComplexQuery($group[0]);
}
else
{
$query = $exprbuilder->buildSimpleQuery($op, $group);
}
if (empty($query))
{
return array();
}
$indexer = Indexer::get();
global $default;
$default->log->debug("SEARCH LUCENE: $query");
$results = $indexer->query($query);
foreach($results as $item)
{
$item->Rank = $exprbuilder->getRanking($item);
$exprbuilder->setQuery($query);
//$item->Text = $exprbuilder->getResultText($item); ?? wipe - done at indexer level
}
return $results;
}
public function evaluate($context = ExprContext::DOCUMENT_AND_FOLDER)
{
if ($context == ExprContext::DOCUMENT_AND_FOLDER)
{
$docs = $this->evaluate(ExprContext::DOCUMENT);
$folders = $this->evaluate(ExprContext::FOLDER);
return array(
'docs' => $docs['docs'],
'folders' => $folders['folders']);
}
$this->setContext($context);
$left = $this->left();
$right = $this->right();
$op = $this->op();
$point = $this->getPoint();
$result = array();
if (empty($point))
{
$point = 'point';
}
$resultContext = ($this->getContext() == ExprContext::DOCUMENT)?'docs':'folders';
if ($point == 'merge')
{
$leftres = $left->evaluate($context);
$rightres = $right->evaluate($context);
switch ($op)
{
case ExprOp::OP_AND:
if ($this->debug) print "\n\nmerge: intersect\n\n";
$result = OpExpr::intersect($leftres, $rightres);
break;
case ExprOp::OP_OR:
if ($this->debug) print "\n\nmerge: union\n\n";
$result = OpExpr::union($leftres, $rightres);
break;
default:
throw new Exception("this condition should not happen");
}
}
elseif ($point == 'point')
{
if ($this->isDBonly())
{
$result[$resultContext] = $this->exec_db_query($op, array($this));
}
elseif ($this->isTextOnly())
{
$result[$resultContext] = $this->exec_text_query($op, array($this));
}
elseif (in_array($op, array(ExprOp::OP_OR, ExprOp::OP_AND)))
{
// do we get to this???
// TODO: remove me please.... the simpleQuery stuff should go???
$db_group = array();
$text_group = array();
$this->explore($left, $right, $db_group, 'db');
$this->explore($left, $right, $text_group, 'text');
$db_result[$resultContext] = $this->exec_db_query($op, $db_group);
$text_result[$resultContext] = $this->exec_text_query($op, $text_group);
switch ($op)
{
case ExprOp::OP_AND:
if ($this->debug) print "\n\npoint: intersect\n\n";
$result[$resultContext] = OpExpr::intersect($db_result, $text_result);
break;
case ExprOp::OP_OR:
if ($this->debug) print "\n\nmerge: union\n\n";
$result[$resultContext] = OpExpr::union($db_result, $text_result);
break;
default:
throw new Exception('how did this happen??');
}
}
else
{
throw new Exception('and this?');
}
}
else
{
// we don't have to do anything
//throw new Exception('Is this reached ever?');
}
$permResults = array();
foreach($result[$resultContext] as $idx=>$item)
{
$permResults[$resultContext][$idx] = $item;
}
return $permResults;
}
public function toViz(&$str, $phase)
{
$expr_id = $this->getExprId();
$left = $this->left();
$right = $this->right();
$hastext = $this->getHasText()?'TEXT':'';
$hasdb = $this->getHasDb()?'DB':'';
switch ($phase)
{
case 0:
$not = $this->not()?'NOT':'';
$str .= "struct$expr_id [style=box, label=\"$expr_id: $not $this->op $this->point $hastext$hasdb\"]\n";
break;
case 1:
$left_id = $left->getExprId();
$str .= "struct$expr_id -> struct$left_id\n";
$right_id = $right->getExprId();
$str .= "struct$expr_id -> struct$right_id\n";
break;
}
$left->toViz($str, $phase);
$right->toViz($str, $phase);
}
}
?>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -