{{ $t('INBOX_MGMT.FINISH.TELEGRAM_QR_INSTRUCTION') }}
-
From 0c2ab7f5e7b349b2aca1529ca14c7c9b7554f257 Mon Sep 17 00:00:00 2001
From: Pranav
Date: Wed, 27 Aug 2025 21:40:28 -0700
Subject: [PATCH 5/5] feat(ee): Setup advanced, performant message search
(#12193)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We now support searching within the actual message content, email
subject lines, and audio transcriptions. This enables a faster, more
accurate search experience going forward. Unlike the standard message
search, which is limited to the last 3 months, this search has no time
restrictions.
The search engine also accounts for small variations in queries. Minor
spelling mistakes, such as searching for slck instead of Slack, will
still return the correct results. It also ignores differences in accents
and diacritics, so searching for Deja vu will match content containing
Déjà vu.
We can also refine searches in the future by criteria such as:
- Searching within a specific inbox
- Filtering by sender or recipient
- Limiting to messages sent by an agent
Fixes https://github.com/chatwoot/chatwoot/issues/11656
Fixes https://github.com/chatwoot/chatwoot/issues/10669
Fixes https://github.com/chatwoot/chatwoot/issues/5910
---
Rake tasks to reindex all the messages.
```sh
bundle exec rake search:all
```
Rake task to reindex messages from one account only
```sh
bundle exec rake search:account ACCOUNT_ID=1
```
---
Gemfile | 4 ++
Gemfile.lock | 12 +++++
Rakefile | 4 ++
.../search/components/MessageContent.vue | 32 ++++++++---
.../components/SearchResultMessagesList.vue | 2 +-
app/models/message.rb | 23 +++++++-
app/services/search_service.rb | 10 ++++
.../v1/accounts/search/_message.json.jbuilder | 13 +----
config/features.yml | 5 ++
config/initializers/searchkick.rb | 14 +++++
.../app/services/enterprise/search_service.rb | 15 ++++++
.../messages/audio_transcription_service.rb | 4 ++
.../app/services/messages/reindex_service.rb | 15 ++++++
enterprise/lib/tasks.rb | 4 ++
enterprise/lib/tasks/search.rake | 49 +++++++++++++++++
lib/chatwoot_app.rb | 4 ++
spec/models/message_spec.rb | 53 +++++++++++++++++++
17 files changed, 242 insertions(+), 21 deletions(-)
create mode 100644 config/initializers/searchkick.rb
create mode 100644 enterprise/app/services/enterprise/search_service.rb
create mode 100644 enterprise/app/services/messages/reindex_service.rb
create mode 100644 enterprise/lib/tasks.rb
create mode 100644 enterprise/lib/tasks/search.rake
diff --git a/Gemfile b/Gemfile
index 615267a77..1148251af 100644
--- a/Gemfile
+++ b/Gemfile
@@ -62,6 +62,10 @@ gem 'redis-namespace'
# super fast record imports in bulk
gem 'activerecord-import'
+gem 'searchkick'
+gem 'opensearch-ruby'
+gem 'faraday_middleware-aws-sigv4'
+
##--- gems for server & infra configuration ---##
gem 'dotenv-rails', '>= 3.0.0'
gem 'foreman'
diff --git a/Gemfile.lock b/Gemfile.lock
index 8fa38a31e..26814db59 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -299,6 +299,9 @@ GEM
net-http-persistent (~> 4.0)
faraday-retry (2.2.1)
faraday (~> 2.0)
+ faraday_middleware-aws-sigv4 (1.0.1)
+ aws-sigv4 (~> 1.0)
+ faraday (>= 2.0, < 3)
fast-mcp (1.5.0)
addressable (~> 2.8)
base64
@@ -601,6 +604,9 @@ GEM
omniauth-rails_csrf_protection (1.0.2)
actionpack (>= 4.2)
omniauth (~> 2.0)
+ opensearch-ruby (3.4.0)
+ faraday (>= 1.0, < 3)
+ multi_json (>= 1.0)
openssl (3.2.0)
orm_adapter (0.5.0)
os (1.1.4)
@@ -802,6 +808,9 @@ GEM
parser
scss_lint (0.60.0)
sass (~> 3.5, >= 3.5.5)
+ searchkick (5.5.2)
+ activemodel (>= 7.1)
+ hashie
securerandom (0.4.1)
seed_dump (3.3.1)
activerecord (>= 4)
@@ -996,6 +1005,7 @@ DEPENDENCIES
facebook-messenger
factory_bot_rails (>= 6.4.3)
faker
+ faraday_middleware-aws-sigv4
fcm
flag_shih_tzu
foreman
@@ -1036,6 +1046,7 @@ DEPENDENCIES
omniauth-google-oauth2 (>= 1.1.3)
omniauth-oauth2
omniauth-rails_csrf_protection (~> 1.0, >= 1.0.2)
+ opensearch-ruby
pg
pg_search
pgvector
@@ -1064,6 +1075,7 @@ DEPENDENCIES
ruby_llm-schema
scout_apm
scss_lint
+ searchkick
seed_dump
sentry-rails (>= 5.19.0)
sentry-ruby
diff --git a/Rakefile b/Rakefile
index e85f91391..591d2c4c6 100644
--- a/Rakefile
+++ b/Rakefile
@@ -4,3 +4,7 @@
require_relative 'config/application'
Rails.application.load_tasks
+
+# Load Enterprise Edition rake tasks if they exist
+enterprise_tasks_path = Rails.root.join('enterprise/lib/tasks.rb').to_s
+require enterprise_tasks_path if File.exist?(enterprise_tasks_path)
diff --git a/app/javascript/dashboard/modules/search/components/MessageContent.vue b/app/javascript/dashboard/modules/search/components/MessageContent.vue
index fef35ac89..9ec425919 100644
--- a/app/javascript/dashboard/modules/search/components/MessageContent.vue
+++ b/app/javascript/dashboard/modules/search/components/MessageContent.vue
@@ -1,5 +1,5 @@