From 6437cdfbe0f611b669ad7625880d0dee75141132 Mon Sep 17 00:00:00 2001
From: Rodrigo Nascimento <rodrigoknascimento@gmail.com>
Date: Wed, 26 Jul 2023 00:16:47 -0300
Subject: [PATCH] fix: Performance issue when using api to create users
 (#29914)

Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com>
Co-authored-by: Kevin Aleman <11577696+KevLehman@users.noreply.github.com>
---
 .changeset/fifty-lizards-jump.md              |  5 ++++
 .../functions/checkEmailAvailability.ts       |  5 +---
 apps/meteor/server/models/raw/Users.js        | 26 ++++++++++++++++---
 3 files changed, 28 insertions(+), 8 deletions(-)
 create mode 100644 .changeset/fifty-lizards-jump.md

diff --git a/.changeset/fifty-lizards-jump.md b/.changeset/fifty-lizards-jump.md
new file mode 100644
index 00000000000..9e25adc8607
--- /dev/null
+++ b/.changeset/fifty-lizards-jump.md
@@ -0,0 +1,5 @@
+---
+"@rocket.chat/meteor": patch
+---
+
+fix: Performance issue when using api to create users
diff --git a/apps/meteor/app/lib/server/functions/checkEmailAvailability.ts b/apps/meteor/app/lib/server/functions/checkEmailAvailability.ts
index 21a5fc36de8..900f299bafe 100644
--- a/apps/meteor/app/lib/server/functions/checkEmailAvailability.ts
+++ b/apps/meteor/app/lib/server/functions/checkEmailAvailability.ts
@@ -1,8 +1,5 @@
-import { escapeRegExp } from '@rocket.chat/string-helpers';
 import { Users } from '@rocket.chat/models';
 
 export const checkEmailAvailability = async function (email: string): Promise<boolean> {
-	return !(await Users.findOne({
-		'emails.address': { $regex: new RegExp(`^${escapeRegExp(email).trim()}$`, 'i') },
-	}));
+	return !(await Users.findOneByEmailAddress(email));
 };
diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js
index 423d4a62789..808157fd6ff 100644
--- a/apps/meteor/server/models/raw/Users.js
+++ b/apps/meteor/server/models/raw/Users.js
@@ -66,6 +66,18 @@ export class UsersRaw extends BaseRaw {
 			{ key: { language: 1 }, sparse: true },
 			{ key: { 'active': 1, 'services.email2fa.enabled': 1 }, sparse: true }, // used by statistics
 			{ key: { 'active': 1, 'services.totp.enabled': 1 }, sparse: true }, // used by statistics
+			// Used for case insensitive queries
+			// @deprecated
+			// Should be converted to unique index later within a migration to prevent errors of duplicated
+			// records. Those errors does not helps to identify the duplicated value so we need to find a
+			// way to help the migration in case it happens.
+			{
+				key: { 'emails.address': 1 },
+				unique: false,
+				sparse: true,
+				name: 'emails.address_insensitive',
+				collation: { locale: 'en', strength: 2, caseLevel: false },
+			},
 		];
 	}
 
@@ -1885,17 +1897,23 @@ export class UsersRaw extends BaseRaw {
 
 	findOneByEmailAddressAndServiceNameIgnoringCase(emailAddress, userId, serviceName, options) {
 		const query = {
-			'emails.address': new RegExp(`^${escapeRegExp(String(emailAddress).trim())}$`, 'i'),
+			'emails.address': String(emailAddress).trim(),
 			[`services.${serviceName}.id`]: userId,
 		};
 
-		return this.findOne(query, options);
+		return this.findOne(query, {
+			collation: { locale: 'en', strength: 2 }, // Case insensitive
+			...options,
+		});
 	}
 
 	findOneByEmailAddress(emailAddress, options) {
-		const query = { 'emails.address': String(emailAddress).trim().toLowerCase() };
+		const query = { 'emails.address': String(emailAddress).trim() };
 
-		return this.findOne(query, options);
+		return this.findOne(query, {
+			collation: { locale: 'en', strength: 2 }, // Case insensitive
+			...options,
+		});
 	}
 
 	findOneAdmin(userId, options) {
-- 
GitLab