From ae8b18530b7e6dee31eb19a42e76dcf0bd9b5360 Mon Sep 17 00:00:00 2001
From: Guillaume Heurtier <guillaume.heurtier@maarch.org>
Date: Tue, 27 Oct 2020 12:01:16 +0100
Subject: [PATCH] FEAT #13271 TIME 6:45 optimize search

---
 migration/20.10/2010.sql                      |  2 ++
 sql/structure.sql                             |  2 ++
 .../contact/controllers/ContactController.php |  2 +-
 .../search/controllers/SearchController.php   | 24 ++++++-------
 .../controllers/AutoCompleteController.php    | 34 +++++++++----------
 5 files changed, 34 insertions(+), 30 deletions(-)

diff --git a/migration/20.10/2010.sql b/migration/20.10/2010.sql
index 8e9dd2479c1..57851cc3456 100755
--- a/migration/20.10/2010.sql
+++ b/migration/20.10/2010.sql
@@ -9,6 +9,8 @@ UPDATE parameters SET param_value_string = '20.10.1' WHERE id = 'database_versio
 
 DROP VIEW IF EXISTS res_view_letterbox;
 
+CREATE EXTENSION unaccent;
+
 /* SENDMAIL */
 DROP TABLE IF EXISTS sendmail;
 
diff --git a/sql/structure.sql b/sql/structure.sql
index 3da57bbc7a3..e03bcc9a582 100755
--- a/sql/structure.sql
+++ b/sql/structure.sql
@@ -13,6 +13,8 @@ SET search_path = public, pg_catalog;
 SET default_tablespace = '';
 SET default_with_oids = false;
 
+CREATE EXTENSION unaccent;
+
 CREATE TABLE actions
 (
   id serial NOT NULL,
diff --git a/src/app/contact/controllers/ContactController.php b/src/app/contact/controllers/ContactController.php
index ebdb9ffb7b4..2b1a25997a7 100755
--- a/src/app/contact/controllers/ContactController.php
+++ b/src/app/contact/controllers/ContactController.php
@@ -75,7 +75,7 @@ class ContactController
         if (!empty($queryParams['search'])) {
             $fields = ['firstname', 'lastname', 'company', 'address_number', 'address_street', 'address_additional1', 'address_additional2', 'address_postcode', 'address_town', 'address_country'];
             $fieldsNumber = count($fields);
-            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
 
             $requestData = AutoCompleteController::getDataForRequest([
                 'search'        => $queryParams['search'],
diff --git a/src/app/search/controllers/SearchController.php b/src/app/search/controllers/SearchController.php
index 378bae2c671..e18883b9ea4 100644
--- a/src/app/search/controllers/SearchController.php
+++ b/src/app/search/controllers/SearchController.php
@@ -324,7 +324,7 @@ class SearchController
                 $args['searchData'] = array_merge($args['searchData'], [$quick, $quick, $quick, $quick, $quick]);
             } else {
                 $fields = ['subject', 'alt_identifier', 'barcode'];
-                $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+                $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
                 $requestDataDocument = AutoCompleteController::getDataForRequest([
                     'search'        => $body['meta']['values'],
                     'fields'        => $fields,
@@ -334,7 +334,7 @@ class SearchController
                 ]);
 
                 $fields = ['title', 'identifier'];
-                $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+                $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
                 $requestDataAttachment = AutoCompleteController::getDataForRequest([
                     'search'        => $body['meta']['values'],
                     'fields'        => $fields,
@@ -381,7 +381,7 @@ class SearchController
                 $args['searchData'][] = $subject;
             } else {
                 $fields = ['subject'];
-                $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+                $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
                 $requestData = AutoCompleteController::getDataForRequest([
                     'search'        => $body['subject']['values'],
                     'fields'        => $fields,
@@ -390,7 +390,7 @@ class SearchController
                     'fieldsNumber'  => 1
                 ]);
                 $subjectGlue = implode(' AND ', $requestData['where']);
-                $attachmentField = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => ['title']]);
+                $attachmentField = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => ['title']]);
                 $subjectGlue = "(($subjectGlue) OR res_id in (select res_id_master from res_attachments where {$attachmentField} and status in ('TRA', 'A_TRA', 'FRZ')))";
                 $args['searchWhere'][] = $subjectGlue;
                 $args['searchData'] = array_merge($args['searchData'], $requestData['data']);
@@ -589,7 +589,7 @@ class SearchController
             $args['searchData'][] = $sendersMatch;
         }
         if (!empty($body['senders']) && !empty($body['senders']['values']) && is_array($body['senders']['values']) && is_string($body['senders']['values'][0])) {
-            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => ['company']]);
+            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => ['company']]);
 
             $requestData = AutoCompleteController::getDataForRequest([
                 'search'       => $body['senders']['values'][0],
@@ -645,7 +645,7 @@ class SearchController
             $args['searchData'][] = $recipientsMatch;
         }
         if (!empty($body['recipients']) && !empty($body['recipients']['values']) && is_array($body['recipients']['values']) && is_string($body['recipients']['values'][0])) {
-            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => ['company']]);
+            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => ['company']]);
 
             $requestData = AutoCompleteController::getDataForRequest([
                 'search'       => $body['recipients']['values'][0],
@@ -856,7 +856,7 @@ class SearchController
                         if (!empty($where)) {
                             $where .= ' OR ';
                         }
-                        $where .= '(item_id = ? AND item_type = ?';
+                        $where .= '((item_id = ? AND item_type = ?)';
                         $data[] = $itemValue['id'];
                         $data[] = $itemValue['type'] == 'user' ? 'user_id' : 'entity_id';
 
@@ -920,7 +920,7 @@ class SearchController
                             $args['searchData'][] = $subject;
                         } else {
                             $fields = ["custom_fields->>'{$customFieldId}'"];
-                            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+                            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
                             $requestData = AutoCompleteController::getDataForRequest([
                                 'search'        => $value['values'],
                                 'fields'        => $fields,
@@ -995,7 +995,7 @@ class SearchController
                         }
                         $args['searchWhere'][] = '(' . implode(' or ', $contactSearchWhere) . ')';
                     } elseif (!empty($value['values']) && is_array($value['values']) && is_string($value['values'][0])) {
-                        $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => ['company']]);
+                        $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => ['company']]);
 
                         $requestData = AutoCompleteController::getDataForRequest([
                             'search'       => $value['values'],
@@ -1120,7 +1120,7 @@ class SearchController
             $args['searchData'][] = $registeredMailsMatch;
         }
         if (!empty($body['registeredMail_recipient']) && !empty($body['registeredMail_recipient']['values']) && is_array($body['registeredMail_recipient']['values']) && is_string($body['registeredMail_recipient']['values'][0])) {
-            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => ["recipient->>'company'"]]);
+            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => ["recipient->>'company'"]]);
             $requestData = AutoCompleteController::getDataForRequest([
                 'search'       => $body['registeredMail_recipient']['values'][0],
                 'fields'       => $fields,
@@ -1835,7 +1835,7 @@ class SearchController
                 $subject   = trim($body['subject']['values'], '"');
                 $data[]    = $subject;
             } else {
-                $attachmentField = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => ['title']]);
+                $attachmentField = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => ['title']]);
                 $wherePlus = "res_id in (select res_id_master from res_attachments where {$attachmentField} and status in ('TRA', 'A_TRA', 'FRZ'))";
                 $data[]    = "%{$body['subject']['values']}%";
             }
@@ -1858,7 +1858,7 @@ class SearchController
                 $data[] = $quick;
             } else {
                 $fields = ['title', 'identifier'];
-                $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+                $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
                 $requestDataAttachment = AutoCompleteController::getDataForRequest([
                     'search'        => $body['meta']['values'],
                     'fields'        => $fields,
diff --git a/src/core/controllers/AutoCompleteController.php b/src/core/controllers/AutoCompleteController.php
index c8bdc875204..9c29007e111 100755
--- a/src/core/controllers/AutoCompleteController.php
+++ b/src/core/controllers/AutoCompleteController.php
@@ -49,7 +49,7 @@ class AutoCompleteController
         }
 
         $fields = ['firstname', 'lastname'];
-        $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+        $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
 
         $requestData = AutoCompleteController::getDataForRequest([
             'search'        => $queryParams['search'],
@@ -174,7 +174,7 @@ class AutoCompleteController
             }
 
             $fieldsNumber = count($fields);
-            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
 
             $requestData = AutoCompleteController::getDataForRequest([
                 'search'        => $queryParams['search'],
@@ -214,7 +214,7 @@ class AutoCompleteController
 
             $nbFields = count($fields);
 
-            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
             $requestData = AutoCompleteController::getDataForRequest([
                 'search'        => $queryParams['search'],
                 'fields'        => $fields,
@@ -258,7 +258,7 @@ class AutoCompleteController
 
             $nbFields = count($fields);
 
-            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
             $requestData = AutoCompleteController::getDataForRequest([
                 'search'        => $queryParams['search'],
                 'fields'        => $fields,
@@ -295,7 +295,7 @@ class AutoCompleteController
         $autocompleteContactsGroups = [];
         if (empty($queryParams['noContactsGroups'])) {
             $fields = ['label'];
-            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
             $requestData = AutoCompleteController::getDataForRequest([
                 'search'        => $queryParams['search'],
                 'fields'        => $fields,
@@ -347,7 +347,7 @@ class AutoCompleteController
             $entities = EntityModel::getAllEntitiesByUserId(['userId' => $GLOBALS['id']]);
 
             $fields = ['users.firstname', 'users.lastname'];
-            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
 
             $requestData = AutoCompleteController::getDataForRequest([
                 'search'        => $data['search'],
@@ -371,7 +371,7 @@ class AutoCompleteController
 
             if (count($users) < self::LIMIT) {
                 $fields = ['users.firstname', 'users.lastname'];
-                $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+                $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
 
                 $requestData = AutoCompleteController::getDataForRequest([
                     'search'        => $data['search'],
@@ -448,7 +448,7 @@ class AutoCompleteController
 
         if (!empty($queryParams['search'])) {
             $fields = ['users.firstname', 'users.lastname'];
-            $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+            $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
 
             $requestData = AutoCompleteController::getDataForRequest([
                 'search'        => $queryParams['search'],
@@ -489,7 +489,7 @@ class AutoCompleteController
         }
 
         $fields = ['entity_label'];
-        $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+        $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
 
         $requestData = AutoCompleteController::getDataForRequest([
             'search'        => $queryParams['search'],
@@ -560,7 +560,7 @@ class AutoCompleteController
         }
 
         $fieldsNumber = count($fields);
-        $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+        $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
 
         $requestData = AutoCompleteController::getDataForRequest([
             'search'        => $data['search'],
@@ -594,7 +594,7 @@ class AutoCompleteController
             return $response->withStatus(400)->withJson(['errors' => 'Query params search is empty']);
         }
 
-        $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => ['company']]);
+        $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => ['company']]);
         $contacts = ContactModel::get([
             'select'    => [
                 'id', 'company', 'address_number as "addressNumber"', 'address_street as "addressStreet"',
@@ -754,7 +754,7 @@ class AutoCompleteController
         }
 
         $fieldsNumber = count($fields);
-        $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+        $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
 
         $requestData = AutoCompleteController::getDataForRequest([
             'search'        => $queryParams['search'],
@@ -863,7 +863,7 @@ class AutoCompleteController
 
         $arrScopedFoldersIds = array_column($scopedFolders, 'id');
 
-        $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => ['label']]);
+        $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => ['label']]);
 
         $selectedFolders = FolderModel::get([
             'where'    => ["{$fields} AND id in (?)"],
@@ -893,7 +893,7 @@ class AutoCompleteController
         }
 
         $fields = ['label'];
-        $fields = AutoCompleteController::getUnsensitiveFieldsForRequest(['fields' => $fields]);
+        $fields = AutoCompleteController::getInsensitiveFieldsForRequest(['fields' => $fields]);
 
         $requestData = AutoCompleteController::getDataForRequest([
             'search'        => $data['search'],
@@ -953,15 +953,15 @@ class AutoCompleteController
         return ['where' => $args['where'], 'data' => $args['data']];
     }
 
-    public static function getUnsensitiveFieldsForRequest(array $args)
+    public static function getInsensitiveFieldsForRequest(array $args)
     {
         ValidatorModel::notEmpty($args, ['fields']);
         ValidatorModel::arrayType($args, ['fields']);
 
         $fields = [];
         foreach ($args['fields'] as $key => $field) {
-            $fields[$key] = "translate({$field}, 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûýýþÿŔŕ', 'aaaaaaaceeeeiiiidnoooooouuuuybsaaaaaaaceeeeiiiidnoooooouuuyybyRr')";
-            $fields[$key] .= "ilike translate(?, 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûýýþÿŔŕ', 'aaaaaaaceeeeiiiidnoooooouuuuybsaaaaaaaceeeeiiiidnoooooouuuyybyRr')";
+            $fields[$key] = "unaccent({$field}::text)";
+            $fields[$key] .= " ilike unaccent(?::text)";
         }
         $fields = implode(' OR ', $fields);
         $fields = "({$fields})";
-- 
GitLab