From 1b29600d43ba2d2df2a71c2f8ad6511b8341f781 Mon Sep 17 00:00:00 2001
From: Guillaume Heurtier <guillaume.heurtier@maarch.org>
Date: Fri, 10 Jan 2020 15:40:40 +0100
Subject: [PATCH] FEAT #12754 TIME 8:00 optimized contact migration

---
 migration/19.12/migrateContacts.php | 241 +++++++++++++++++++++-------
 1 file changed, 181 insertions(+), 60 deletions(-)

diff --git a/migration/19.12/migrateContacts.php b/migration/19.12/migrateContacts.php
index 71295de5e2f..0c08774af4d 100755
--- a/migration/19.12/migrateContacts.php
+++ b/migration/19.12/migrateContacts.php
@@ -96,6 +96,14 @@ foreach ($customs as $custom) {
 
     $firstMan = \User\models\UserModel::get(['select' => ['id'], 'orderBy' => ['id'], 'limit' => 1, 'where' => ['status = ?'], 'data' => ['OK']]);
 
+    $table = "contacts (id, civility, firstname, lastname, company, department, function, address_number, address_street,"
+        . "address_additional1, address_additional2, address_postcode, address_town, address_country, email, phone,"
+        . "communication_means, notes, creator, creation_date, modification_date, enabled, external_id)";
+
+    $contactInfoSeparator = "\t";
+
+    $id = 1;
+    $contacts = [];
     $debutMigrateInProgress = microtime(true);
     foreach ($contactsInfo as $contactInfo) {
         $oldContactId = $contactInfo['contact_id'];
@@ -186,59 +194,199 @@ foreach ($customs as $custom) {
         unset($contactInfo['contact_type_label']);
         unset($contactInfo['contact_purpose_label']);
         unset($contactInfo['society_short']);
-        $id = \Contact\models\ContactModel::create($contactInfo);
+
+        $contactInfo['id'] = $id;
+
+        foreach ($contactInfo as $key => $item) {
+            $contactInfo[$key] = (str_replace("\t", " ", $contactInfo[$key]));
+            $contactInfo[$key] = $contactInfo[$key] ?? "NULL";
+            if (empty($contactInfo[$key])) {
+                $contactInfo[$key] = "NULL";
+            }
+        }
+
+        $contact = $id . $contactInfoSeparator
+            . $contactInfo['civility'] . $contactInfoSeparator
+            . $contactInfo['firstname'] . $contactInfoSeparator
+            . $contactInfo['lastname'] . $contactInfoSeparator
+            . $contactInfo['company'] . $contactInfoSeparator
+            . $contactInfo['departement'] . $contactInfoSeparator
+            . $contactInfo['function'] . $contactInfoSeparator
+            . $contactInfo['address_number'] . $contactInfoSeparator
+            . $contactInfo['address_street'] . $contactInfoSeparator
+            . $contactInfo['address_additional1'] . $contactInfoSeparator
+            . $contactInfo['address_additional2'] . $contactInfoSeparator
+            . $contactInfo['address_postcode'] . $contactInfoSeparator
+            . $contactInfo['address_town'] . $contactInfoSeparator
+            . $contactInfo['address_country'] . $contactInfoSeparator
+            . $contactInfo['email'] . $contactInfoSeparator
+            . $contactInfo['phone'] . $contactInfoSeparator
+            . $contactInfo['communication_means'] . $contactInfoSeparator
+            . $contactInfo['notes'] . $contactInfoSeparator
+            . $contactInfo['creator'] . $contactInfoSeparator
+            . $contactInfo['creation_date'] . $contactInfoSeparator
+            . $contactInfo['modification_date'] . $contactInfoSeparator
+            . $contactInfo['enabled'] . $contactInfoSeparator
+            . $contactInfo['external_id'];
+
+        if (strpos($contact, "\r")) {
+            $contact = str_replace("\r", " ", $contact);
+            $contact = str_replace("\n", " ", $contact);
+        }
+
+        $contacts[] = $contact;
+
+        $ids[$id] = ['oldAddressId' => $oldAddressId, 'oldContactId' => $oldContactId];
 
         migrateCustomField(['newContactId' => $id, 'contactCustomInfo' => $contactCustomInfo, 'newCustomFields' => $newCustomFields]);
-        \SrcCore\models\DatabaseModel::update([
-            'set'   => ['contact_id' => $id],
-            'table' => 'acknowledgement_receipts',
-            'where' => ['contact_address_id = ?'],
-            'data'  => [$oldAddressId]
-        ]);
-        \SrcCore\models\DatabaseModel::update([
-            'set'   => ['contact_id' => $id],
-            'table' => 'contacts_groups_lists',
-            'where' => ['contact_addresses_id = ?'],
-            'data'  => [$oldAddressId]
-        ]);
+
         $currentValuesContactRes = migrateContactRes(['oldAddressId' => $oldAddressId, 'oldContactId' => $oldContactId, 'newContactId' => $id]);
-        \SrcCore\models\DatabaseModel::update([
-            'set'   => ['item_id' => $id, 'type' => 'contact_v3'],
-            'table' => 'resource_contacts',
-            'where' => ['item_id = ?', 'type = ?'],
-            'data'  => [$oldAddressId, 'contact']
-        ]);
-        \SrcCore\models\DatabaseModel::update([
-            'set'   => ['recipient_id' => $id, 'recipient_type' => 'contact'],
-            'table' => 'res_attachments',
-            'where' => ['dest_contact_id = ?', 'dest_address_id = ?'],
-            'data'  => [$oldContactId, $oldAddressId],
-        ]);
-        $currentValuesResletterbox = migrateResletterbox(['oldAddressId' => $oldAddressId, 'newContactId' => $id]);
-        $aValues = array_merge($aValues, $currentValuesContactRes, $currentValuesResletterbox);
+
+        $aValues = array_merge($aValues, $currentValuesContactRes);
 
         $migrated++;
 
         if ($migrated % 5000 == 0) {
-            pg_copy_from($databaseConnection, 'resource_contacts (res_id, item_id, type, mode)', $aValues, "\t", "\\\\N");
+            $finMigrateInProgress = microtime(true);
+            $delaiInProgress = $finMigrateInProgress - $debutMigrateInProgress;
+            echo "Migration En cours : ".$delaiInProgress." secondes.\n";
+            $debutMigrateInProgress = microtime(true);
+            printf($migrated . " contact(s) migré(s) - En cours...\n");
+        }
+
+        if ($migrated % 50000 == 0) {
+            echo "Migration de 50000 contacts...\n";
+
+            $beforeCopyContacts = microtime(true);
+            pg_copy_from($databaseConnection, $table, $contacts, $contactInfoSeparator, "NULL");
+            $afterCopyContacts = microtime(true);
+            $copyTimeContacts = $afterCopyContacts - $beforeCopyContacts;
+            echo "Temps copy contacts = $copyTimeContacts\n";
+
+            pg_copy_from($databaseConnection, 'resource_contacts (res_id, item_id, type, mode)', $aValues, "\t", 	"\\\\N");
             $finMigrateInProgress = microtime(true);
             $delaiInProgress = $finMigrateInProgress - $debutMigrateInProgress;
             echo "Migration En cours : ".$delaiInProgress." secondes.\n";
             $debutMigrateInProgress = microtime(true);
             printf($migrated . " contact(s) migré(s) - En cours...\n");
             $aValues = [];
+
+            $contacts = [];
         }
+
+        $id++;
     }
 
     if (!empty($aValues)) {
+        $beforeCopy = microtime(true);
         pg_copy_from($databaseConnection, 'resource_contacts (res_id, item_id, type, mode)', $aValues, "\t", "\\\\N");
-        $finMigrateInProgress = microtime(true);
-        $delaiInProgress = $finMigrateInProgress - $debutMigrateInProgress;
-        echo "Dernière Migration En cours : ".$delaiInProgress." secondes.\n";
-        $debutMigrateInProgress = microtime(true);
-        printf($migrated . " contact(s) migré(s) - Fin...\n");
+        $afterCopy = microtime(true);
+        $copyTime = $afterCopy - $beforeCopy;
+        echo "Temps copy resource contacts = $copyTime secondes\n";
     }
 
+    if (!empty($contacts)) {
+        $beforeCopyContacts = microtime(true);
+        pg_copy_from($databaseConnection, $table, $contacts, $contactInfoSeparator, "NULL");
+        $afterCopyContacts = microtime(true);
+        $copyTimeContacts = $afterCopyContacts - $beforeCopyContacts;
+        echo "Temps copy contacts = $copyTimeContacts\n";
+    }
+
+
+
+    $beforeUpdates = microtime(true);
+    $valuesOldAddress = '';
+    $firstDone = false;
+    foreach ($ids as $newId => $value) {
+        $oldAddressId = $value['oldAddressId'];
+        if ($firstDone) {
+            $valuesOldAddress .= ', ';
+        }
+        $valuesOldAddress .= "( $newId , $oldAddressId)";
+        if (!$firstDone) {
+            $firstDone = true;
+        }
+    }
+
+    // Migrate addresses in res_letterbox
+    $query = "insert into resource_contacts (res_id, item_id, type, mode)
+    select res_id, tmp.new_id, 'contact_v3' as type,
+       case
+           when category_id = 'outgoing' then 'recipient'
+           else 'sender'
+       end as mode
+    from res_letterbox,
+         (values 
+             $valuesOldAddress
+         ) as tmp(new_id, old_address_id)
+    where tmp.old_address_id = res_letterbox.address_id";
+    pg_query($databaseConnection, $query);
+
+
+    // Acknowledgement Receipts
+    $query = "update acknowledgement_receipts as ar set
+            contact_id = tmp.old_address_id
+        from (values
+               $valuesOldAddress 
+            ) as tmp(new_id, old_address_id) 
+        where ar.contact_address_id = tmp.old_address_id";
+    pg_query($databaseConnection, $query);
+
+
+    // Group list
+    $query = "update contacts_groups_lists as cgl set
+            contact_id = tmp.old_address_id
+        from (values
+               $valuesOldAddress
+            ) as tmp(new_id, old_address_id) 
+        where cgl.contact_addresses_id = tmp.old_address_id";
+
+    pg_query($databaseConnection, $query);
+
+
+    // Resources contacts
+    $query = "update resource_contacts as rc set
+        item_id = tmp.old_address_id, type = 'contact_v3'
+    from (values
+           $valuesOldAddress
+        ) as tmp(new_id, old_address_id) 
+    where rc.item_id = tmp.old_address_id and type = 'contact'";
+    pg_query($databaseConnection, $query);
+
+    $valuesOld= '';
+    $firstDone = false;
+    foreach ($ids as $newId => $value) {
+        $oldAddressId = $value['oldAddressId'];
+        $oldContactId = $value['oldContactId'];
+        if ($firstDone) {
+            $valuesOld .= ', ';
+        }
+        $valuesOld .= "( $newId , $oldAddressId, $oldContactId)";
+        if (!$firstDone) {
+            $firstDone = true;
+        }
+    }
+
+    // Res attach
+    $query = "update res_attachments as ra set
+        recipient_id = tmp.old_address_id, recipient_type = 'contact_v3'
+    from (values
+           $valuesOld
+        ) as tmp(new_id, old_address_id, old_contact_id) 
+    where ra.dest_contact_id = tmp.old_contact_id and ra.dest_address_id = tmp.old_address_id";
+    pg_query($databaseConnection, $query);
+
+    $afterUpdates = microtime(true);
+    $updatesTime = $afterUpdates - $beforeUpdates;
+    echo "Temps updates = $updatesTime secondes\n";
+
+    $finMigrateInProgress = microtime(true);
+    $delaiInProgress = $finMigrateInProgress - $debutMigrateInProgress;
+    echo "Dernière Migration En cours : ".$delaiInProgress." secondes.\n";
+    $debutMigrateInProgress = microtime(true);
+    printf($migrated . " contact(s) migré(s) - Fin...\n");
+
     $debutEndMigrate = microtime(true);
     migrateContactRes_Users(['firstManId' => $firstMan[0]['id'], 'databaseConnection' => $databaseConnection]);
     migrateResletterbox_Users(['firstManId' => $firstMan[0]['id'], 'databaseConnection' => $databaseConnection]);
@@ -359,33 +507,6 @@ function migrateContactRes($args = [])
     return $aValues;
 }
 
-function migrateResletterbox($args = [])
-{
-    $resInfo = \SrcCore\models\DatabaseModel::select([
-        'select' => ['res_id', 'category_id'],
-        'table'  => ['res_letterbox'],
-        'where'  => ['address_id = ?'],
-        'data'   => [$args['oldAddressId']],
-    ]);
-
-    $aValues = [];
-    foreach ($resInfo as $value) {
-        $mode = 'sender';
-        if ($value['category_id'] == 'outgoing') {
-            $mode = 'recipient';
-        }
-
-        $aValues[] = implode("\t", [
-            $value['res_id'],
-            $args['newContactId'],
-            'contact_v3',
-            $mode
-        ]) . "\n";
-    }
-
-    return $aValues; 
-}
-
 function migrateContactRes_Users($args = [])
 {
     \SrcCore\models\DatabaseModel::beginTransaction();
-- 
GitLab