Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion phpunit/deprecated-searchoptions/ComputerAntivirus.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"available_searchtypes": [
"contains",
"notcontains",
"matches",
"equals",
"notequals",
"empty"
Expand Down Expand Up @@ -46,4 +47,4 @@
],
"uid": "ComputerAntivirus.signature_version"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"available_searchtypes": [
"contains",
"notcontains",
"matches",
"equals",
"notequals",
"empty"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"available_searchtypes": [
"contains",
"notcontains",
"matches",
"equals",
"notequals",
"empty",
Expand All @@ -49,4 +50,4 @@
],
"uid": "Computer_SoftwareLicense.Computer.name"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@
"available_searchtypes": [
"contains",
"notcontains",
"matches",
"equals",
"notequals",
"empty"
],
"uid": "Computer_SoftwareVersion.SoftwareVersion.name"
}
}
}
72 changes: 72 additions & 0 deletions phpunit/functional/SearchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,48 @@ public function testSearchAllMeta()
}
}

public function testMatchesSearchOption()
{
$classes = $this->getSearchableClasses();
foreach ($classes as $class) {
$item = new $class();
$name = '_test_matches_' . uniqid();
// Test only if the name field exists
if ($item->fields && array_key_exists('name', $item->fields)) {
$id = $item->add([
'name' => $name,
'entities_id' => 0,
'is_recursive' => 1,
]);
$this->assertGreaterThan(0, $id);

$search_params = [
'is_deleted' => 0,
'start' => 0,
'criteria' => [
[
'field' => '1', // name
'searchtype' => 'matches',
'value' => $name,
],
],
];
$data = $this->doSearch($class, $search_params);

$found = false;
if (isset($data['data']['rows'])) {
foreach ($data['data']['rows'] as $row) {
if (isset($row['name']) && $row['name'] === $name) {
$found = true;
break;
}
}
}
$this->assertTrue($found, $class);
}
}
}

/**
* Get criterion params for corresponding SO.
*
Expand Down Expand Up @@ -2571,6 +2613,16 @@ public static function providerAddWhere()
'meta' => false,
'expected' => "(`glpi_users_users_id_supervisor`.`id` = '5')",
],
[
'link' => ' ',
'nott' => 0,
'itemtype' => User::class,
'ID' => 99,
'searchtype' => 'matches',
'val' => 'glpi',
'meta' => false,
'expected' => "(`glpi_users_users_id_supervisor`.`name` = 'glpi')",
],
[
'link' => ' AND ',
'nott' => 0,
Expand Down Expand Up @@ -2651,6 +2703,16 @@ public static function providerAddWhere()
'meta' => false,
'expected' => "AND (INET_ATON(`glpi_ipaddresses`.`name`) < INET_ATON('192.168.1.10'))",
],
[
'link' => ' AND ',
'nott' => 0,
'itemtype' => \NetworkName::class,
'ID' => 13, // Search ID 13 (IPAddress name field)
'searchtype' => 'matches',
'val' => '192.168.1.10',
'meta' => false,
'expected' => "AND (`glpi_ipaddresses`.`name` = '192.168.1.10')",
],
[
'link' => ' AND ',
'nott' => 0,
Expand All @@ -2671,6 +2733,16 @@ public static function providerAddWhere()
'meta' => false,
'expected' => "((`glpi_computers`.`name` = '') OR `glpi_computers`.`name` IS NULL)",
],
[
'link' => ' ',
'nott' => 0,
'itemtype' => Computer::class,
'ID' => 1,
'searchtype' => 'matches',
'val' => 'My computer',
'meta' => false,
'expected' => "(`glpi_computers`.`name` = 'My computer')",
],
];
}

Expand Down
2 changes: 1 addition & 1 deletion phpunit/web/APIRestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ public function testListSearchOptions()
$this->assertIsArray($data[1]['available_searchtypes']);

$this->assertSame(
['contains', 'notcontains', 'equals', 'notequals', 'empty'],
['contains', 'notcontains', 'matches', 'equals', 'notequals', 'empty'],
$data[1]['available_searchtypes']
);
}
Expand Down
16 changes: 14 additions & 2 deletions src/Glpi/Search/Provider/SQLProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*
* @copyright 2015-2025 Teclib' and contributors.
* @copyright 2003-2014 by the INDEPNET Development Team.
* @copyright 2025 Kyndryl Inc.
* @licence https://www.gnu.org/licenses/gpl-3.0.html
*
* ---------------------------------------------------------------------
Expand Down Expand Up @@ -1336,6 +1337,10 @@ public static function getWhereCriteria($nott, $itemtype, $ID, $searchtype, $val
$SEARCH = null;
}
break;

case "matches":
$SEARCH = [$nott ? "<>" : "=", $val];
break;
}

//Check in current item if a specific where is defined
Expand Down Expand Up @@ -1434,6 +1439,9 @@ public static function getWhereCriteria($nott, $itemtype, $ID, $searchtype, $val
$criteria['OR'][] = ["$table.id" => null];
}

return $criteria;
} elseif ($searchtype === 'matches') {
$append_criterion_with_search($criteria['OR'], "$table.$field");
return $criteria;
}
return [new QueryExpression(self::makeTextCriteria("`$table`.`$field`", $val, $nott, ''))];
Expand Down Expand Up @@ -1821,7 +1829,7 @@ public static function getWhereCriteria($nott, $itemtype, $ID, $searchtype, $val
// Condition will be handled by the subquery
break;
}
if (in_array($searchtype, ['equals', 'notequals', 'under', 'notunder', 'empty'])) {
if (in_array($searchtype, ['equals', 'notequals', 'under', 'notunder', 'empty', 'matches'])) {
if ($searchtype === 'empty' && $opt["field"] === 'name') {
$l = $nott ? 'AND' : 'OR';
$criteria = [
Expand All @@ -1832,6 +1840,9 @@ public static function getWhereCriteria($nott, $itemtype, $ID, $searchtype, $val
],
];
$append_criterion_with_search($criteria[$l], $tocompute);
} elseif ($searchtype === 'matches') {
$criteria = [];
$append_criterion_with_search($criteria, $tocompute);
} else {
$criteria = [];
$append_criterion_with_search($criteria, "$table.id");
Expand Down Expand Up @@ -2248,13 +2259,14 @@ public static function getWhereCriteria($nott, $itemtype, $ID, $searchtype, $val
}

// Default case
if (in_array($searchtype, ['equals', 'notequals','under', 'notunder'])) {
if (in_array($searchtype, ['equals', 'notequals','under', 'notunder', 'matches'])) {
$criteria = ['OR' => []];
if (
(!isset($opt['searchequalsonfield'])
|| !$opt['searchequalsonfield'])
&& ($itemtype == AllAssets::getType()
|| $table != $itemtype::getTable())
&& $searchtype !== 'matches'
) {
$append_criterion_with_search($criteria['OR'], "$table.id");
} else {
Expand Down
6 changes: 6 additions & 0 deletions src/Glpi/Search/SearchOption.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*
* @copyright 2015-2025 Teclib' and contributors.
* @copyright 2003-2014 by the INDEPNET Development Team.
* @copyright 2025 Kyndryl Inc.
* @licence https://www.gnu.org/licenses/gpl-3.0.html
*
* ---------------------------------------------------------------------
Expand Down Expand Up @@ -452,6 +453,10 @@ public static function getActionsFor($itemtype, $field_num)
}
foreach ($actions['searchopt']['searchtype'] as $searchtype) {
switch ($searchtype) {
case "matches":
$actions['matches'] = __('matches');
break;

case "equals":
$actions['equals'] = __('is');
break;
Expand Down Expand Up @@ -582,6 +587,7 @@ public static function getActionsFor($itemtype, $field_num)
$actions = [
'contains' => __('contains'),
'notcontains' => __('not contains'),
'matches' => __('matches'),
'equals' => __('is'),
'notequals' => __('is not'),
'empty' => __('is empty'),
Expand Down
Loading