N°1485 - Search: "Undefined" on hierarchical key not working

SVN:trunk[5873]
This commit is contained in:
Eric Espié
2018-06-14 14:56:42 +00:00
parent 5b3d7e2354
commit 5a4375cb71
5 changed files with 167 additions and 107 deletions

View File

@@ -550,27 +550,27 @@ class BinaryExpression extends Expression
$oAttDef = $oRightExpr->GetAttDef($oSearch->GetJoinedClasses());
$aCriteriaLeft = $oLeftExpr->GetCriterion($oSearch, $aArgs, $bRetrofitParams, $oAttDef);
$aCriteria = array_merge($aCriteriaRight, $aCriteriaLeft);
// switch left and right expressions so reverse the operator
// Note that the operation is the same so < becomes > and not >=
switch ($this->GetOperator())
{
case '>':
$aCriteria['operator'] = '<';
$sOperator = '<';
break;
case '<':
$aCriteria['operator'] = '>';
$sOperator = '>';
break;
case '>=':
$aCriteria['operator'] = '<=';
$sOperator = '<=';
break;
case '<=':
$aCriteria['operator'] = '>=';
$sOperator = '>=';
break;
default:
$aCriteria['operator'] = $this->GetOperator();
$sOperator = $this->GetOperator();
break;
}
$aCriteria = self::MergeCriteria($aCriteriaRight, $aCriteriaLeft, $sOperator);
}
else
{
@@ -579,8 +579,7 @@ class BinaryExpression extends Expression
$oAttDef = $oLeftExpr->GetAttDef($oSearch->GetJoinedClasses());
$aCriteriaRight = $oRightExpr->GetCriterion($oSearch, $aArgs, $bRetrofitParams, $oAttDef);
$aCriteria = array_merge($aCriteriaLeft, $aCriteriaRight);
$aCriteria['operator'] = $this->GetOperator();
$aCriteria = self::MergeCriteria($aCriteriaLeft, $aCriteriaRight, $this->GetOperator());
}
$aCriteria['oql'] = $this->Render($aArgs, $bRetrofitParams);
$aCriteria['label'] = $this->Display($oSearch, $aArgs, $oAttDef);
@@ -593,6 +592,30 @@ class BinaryExpression extends Expression
return $aCriteria;
}
protected static function MergeCriteria($aCriteriaLeft, $aCriteriaRight, $sOperator)
{
$aCriteriaOverride = array();
$aCriteriaOverride['operator'] = $sOperator;
if ($sOperator == 'OR')
{
if (isset($aCriteriaLeft['ref']) && isset($aCriteriaRight['ref']) && ($aCriteriaLeft['ref'] == $aCriteriaRight['ref']))
{
if (isset($aCriteriaLeft['widget']) && isset($aCriteriaRight['widget']) && ($aCriteriaLeft['widget'] == AttributeDefinition::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY) && ($aCriteriaRight['widget'] == AttributeDefinition::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY))
{
$aCriteriaOverride['operator'] = 'IN';
$aCriteriaOverride['is_hierarchical'] = true;
if (isset($aCriteriaLeft['values']) && isset($aCriteriaRight['values']))
{
$aCriteriaOverride['values'] = array_merge($aCriteriaLeft['values'], $aCriteriaRight['values']);
}
}
}
}
return array_merge($aCriteriaLeft, $aCriteriaRight, $aCriteriaOverride);
}
}
@@ -697,7 +720,11 @@ class ScalarExpression extends UnaryExpression
{
/** @var AttributeExternalKey $oAttDef */
$sTarget = $oAttDef->GetTargetClass();
$oObj = MetaModel::GetObject($sTarget, $this->m_value);
$oObj = MetaModel::GetObject($sTarget, $this->m_value, false);
if (empty($oObj))
{
return Dict::S('Enum:Undefined');
}
return $oObj->Get("friendlyname");
} catch (CoreException $e)

View File

@@ -216,95 +216,23 @@ class CriterionToOQL extends CriterionConversionAbstract
return "1";
}
$bFilterOnUndefined = false;
$oAttDef = null;
try
{
$aAttributeDefs = MetaModel::ListAttributeDefs($sClass);
if (array_key_exists($sAttCode, $aAttributeDefs))
{
$oAttDef = $aAttributeDefs[$sAttCode];
if ($oAttDef instanceof AttributeEnum)
{
$aAllowedValues = SearchForm::GetFieldAllowedValues($oAttDef);
if (array_key_exists('values', $aAllowedValues))
{
// Can't invert the test if NULL is allowed
if (!$oAttDef->IsNullAllowed())
{
$aAllowedValues = $aAllowedValues['values'];
if (count($aValues) == count($aAllowedValues))
{
// All entries are selected
return "1";
}
// more selected values than remaining so use NOT IN
else
{
if (count($aValues) > (count($aAllowedValues) / 2))
{
foreach($aValues as $aValue)
{
unset($aAllowedValues[$aValue['value']]);
}
$sInList = implode("','", array_keys($aAllowedValues));
return "({$sRef} NOT IN ('$sInList'))";
}
}
}
// search for "undefined"
for($i = 0; $i < count($aValues); $i++)
{
$aValue = $aValues[$i];
if (isset($aValue['value']) && ($aValue['value'] === 'null'))
{
$bFilterOnUndefined = true;
unset($aValues[$i]);
break;
}
}
}
}
}
} catch (Exception $e)
{
}
$aInValues = array();
foreach($aValues as $aValue)
catch (\CoreException $e)
{
$aInValues[] = $aValue['value'];
return "1";
}
$sInList = implode("','", $aInValues);
if ($bFilterOnUndefined)
if (array_key_exists($sAttCode, $aAttributeDefs))
{
$sFilterOnUndefined = "ISNULL({$sRef})";
if (count($aValues) === 0)
{
$sCondition = $sFilterOnUndefined;
}
elseif (count($aInValues) == 1)
{
// Add 'AND 1' to group the 'OR' inside an AND list for OQL parsing
$sCondition = "((({$sRef} = '$sInList') OR {$sFilterOnUndefined}) AND 1)";
}
else
{
// Add 'AND 1' to group the 'OR' inside an AND list for OQL parsing
$sCondition = "(({$sRef} IN ('$sInList') OR {$sFilterOnUndefined}) AND 1)";
}
}
elseif (count($aInValues) == 1)
{
$sCondition = "({$sRef} = '$sInList')";
}
else
{
$sCondition = "({$sRef} IN ('$sInList'))";
$oAttDef = $aAttributeDefs[$sAttCode];
}
// Hierarchical keys
$sHierarchicalKeyCode = false;
$sTargetClass = '';
if (isset($oAttDef) && $oAttDef->IsExternalKey() && (!isset($aCriteria['is_hierarchical']) || ($aCriteria['is_hierarchical'] !== false)))
{
if ($oAttDef instanceof AttributeExternalKey)
@@ -321,26 +249,126 @@ class CriterionToOQL extends CriterionConversionAbstract
try
{
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
if ($sHierarchicalKeyCode !== false)
{
$oFilter = new DBObjectSearch($sTargetClass);
$sFilterAlias = $oFilter->GetClassAlias();
$sCondition = str_replace("$sRef", $sFilterAlias.'.id', $sCondition);
$oCondition = Expression::FromOQL($sCondition);
$oFilter->AddConditionExpression($oCondition);
$oHKFilter = new DBObjectSearch($sTargetClass);
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
// Use the 'below' operator by default
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
$sCondition = '1';
}
} catch (Exception $e)
}
catch (\CoreException $e)
{
}
}
$sFilterOnUndefined = '';
if ($oAttDef instanceof AttributeEnum)
{
$aAllowedValues = SearchForm::GetFieldAllowedValues($oAttDef);
if (array_key_exists('values', $aAllowedValues))
{
// Can't invert the test if NULL is allowed
if (!$oAttDef->IsNullAllowed())
{
$aAllowedValues = $aAllowedValues['values'];
if (count($aValues) == count($aAllowedValues))
{
// All entries are selected
return "1";
}
// more selected values than remaining so use NOT IN
else
{
if (count($aValues) > (count($aAllowedValues) / 2))
{
foreach($aValues as $aValue)
{
unset($aAllowedValues[$aValue['value']]);
}
$sInList = implode("','", array_keys($aAllowedValues));
return "({$sRef} NOT IN ('$sInList'))";
}
}
}
// search for "undefined"
for($i = 0; $i < count($aValues); $i++)
{
$aValue = $aValues[$i];
if (isset($aValue['value']) && ($aValue['value'] === 'null'))
{
$sFilterOnUndefined = "ISNULL({$sRef})";
unset($aValues[$i]);
break;
}
}
}
}
if ($sHierarchicalKeyCode !== false)
{
// search for "undefined"
for($i = 0; $i < count($aValues); $i++)
{
$aValue = $aValues[$i];
if (isset($aValue['value']) && ($aValue['value'] === '0'))
{
$sFilterOnUndefined = "({$sRef} = '0')";
unset($aValues[$i]);
break;
}
}
}
$aInValues = array();
foreach($aValues as $aValue)
{
$aInValues[] = $aValue['value'];
}
$sInList = implode("','", $aInValues);
$sCondition = '1';
if (count($aInValues) == 1)
{
$sCondition = "({$sRef} = '$sInList')";
}
elseif (count($aInValues) > 1)
{
$sCondition = "({$sRef} IN ('$sInList'))";
}
// Hierarchical keys
try
{
if ($sHierarchicalKeyCode !== false)
{
// Add all the joins for hierarchical key
$oFilter = new DBObjectSearch($sTargetClass);
$sFilterAlias = $oFilter->GetClassAlias();
// Filter on hierarchy
$sCondition = str_replace("$sRef", $sFilterAlias.'.id', $sCondition);
$oCondition = Expression::FromOQL($sCondition);
$oFilter->AddConditionExpression($oCondition);
$oHKFilter = new DBObjectSearch($sTargetClass);
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
// Use the 'below' operator by default
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
$oCriteria = $oSearch->GetCriteria();
$aArgs = MetaModel::PrepareQueryArguments(array(), $oSearch->GetInternalParams());
$oSearch->ResetCondition();
$sCondition = $oCriteria->Render($aArgs);
}
} catch (Exception $e)
{
}
if (!empty($sFilterOnUndefined))
{
if (count($aValues) === 0)
{
$sCondition = $sFilterOnUndefined;
}
else
{
// Add 'AND 1' to group the 'OR' inside an AND list for OQL parsing
$sCondition = "(({$sCondition} OR {$sFilterOnUndefined}) AND 1)";
}
}
return $sCondition;
}

View File

@@ -677,7 +677,7 @@ class CriterionToSearchForm extends CriterionConversionAbstract
unset($aCriteria['oql']);
break;
case 'IN':
// Nothing special to do
unset($aCriteria['oql']);
break;
default:
// Unknown operator

View File

@@ -27,7 +27,7 @@ namespace Combodo\iTop\Test\UnitTest;
use PHPUnit\Framework\TestCase;
define('DEBUG_UNIT_TEST', false);
define('DEBUG_UNIT_TEST', true);
class ItopTestCase extends TestCase
{

View File

@@ -432,11 +432,16 @@ class CriterionConversionTest extends ItopDataTestCase
'ExpectedOQL' => "SELECT `Server` FROM Server AS `Server` WHERE ISNULL(`Server`.`nb_u`)",
'ExpectedCriterion' => array(array('widget' => 'numeric', 'operator' => 'empty')),
),
'Hierarchical below' => array(
'Hierarchical below 1' => array(
'OQL' => "SELECT Person AS P JOIN Organization AS Node ON P.org_id = Node.id JOIN Organization AS Root ON Node.parent_id BELOW Root.id WHERE Root.id=1",
'ExpectedOQL' => "SELECT `P` FROM Person AS `P` JOIN Organization AS `Node` ON `P`.org_id = `Node`.id JOIN Organization AS `Root` ON `Node`.parent_id BELOW `Root`.id WHERE (`Root`.`id` = '1')",
'ExpectedCriterion' => array(array('widget' => 'hierarchical_key')),
),
'Hierarchical below 2' => array(
'OQL' => "SELECT `Organization` FROM Organization AS `Organization` JOIN Organization AS `Organization1` ON `Organization`.parent_id = `Organization1`.id JOIN Organization AS `Organization11` ON `Organization1`.parent_id BELOW `Organization11`.id WHERE (((`Organization11`.`id` IN ('2', '4')) OR (`Organization`.`parent_id` = '0')) AND 1)",
'ExpectedOQL' => "SELECT `Organization` FROM Organization AS `Organization` JOIN Organization AS `Organization1` ON `Organization`.parent_id = `Organization1`.id JOIN Organization AS `Organization11` ON `Organization1`.parent_id BELOW `Organization11`.id WHERE (((`Organization11`.`id` IN ('2', '4')) OR (`Organization`.`parent_id` = '0')) AND 1)",
'ExpectedCriterion' => array(array('widget' => 'hierarchical_key')),
),
'IP range' => array(
'OQL' => "SELECT DatacenterDevice AS dev WHERE INET_ATON(dev.managementip) > INET_ATON('10.22.32.224') AND INET_ATON(dev.managementip) < INET_ATON('10.22.32.255')",
'ExpectedOQL' => "SELECT `dev` FROM DatacenterDevice AS `dev` WHERE ((INET_ATON(`dev`.`managementip`) < INET_ATON('10.22.32.255')) AND (INET_ATON(`dev`.`managementip`) > INET_ATON('10.22.32.224')))",