From c4a71f813a783e310b1c8ca59d2390a8d76f6a0c Mon Sep 17 00:00:00 2001 From: saledjenic <86303051+saledjenic@users.noreply.github.com> Date: Wed, 4 Aug 2021 21:31:44 +0200 Subject: [PATCH] feature(@status-go/chat): implement search on sqlcipher (status-go side) (#2282) * feature(@status-go/chat): implement search on sqlcipher (status-go side) Searching messages by some term for a specific channel is added on the side of status-go. Fixes: #2912 * Linting Co-authored-by: Andrea Maria Piana --- protocol/message_persistence.go | 64 +++++++++++++++++++++++++++++++++ protocol/messenger.go | 9 +++++ services/ext/api.go | 11 ++++++ 3 files changed, 84 insertions(+) diff --git a/protocol/message_persistence.go b/protocol/message_persistence.go index dac4cd6e4..9f5a00bc1 100644 --- a/protocol/message_persistence.go +++ b/protocol/message_persistence.go @@ -588,6 +588,70 @@ func (db sqlitePersistence) MessageByChatID(chatID string, currCursor string, li return result, newCursor, nil } +// AllMessageByChatIDWhichMatchPattern returns all messages which match the search +// term, for a given chatID in descending order. +// Ordering is accomplished using two concatenated values: ClockValue and ID. +// These two values are also used to compose a cursor which is returned to the result. +func (db sqlitePersistence) AllMessageByChatIDWhichMatchTerm(chatID string, searchTerm string, caseSensitive bool) ([]*common.Message, error) { + if searchTerm == "" { + return nil, fmt.Errorf("empty search term") + } + + searchCond := "" + if caseSensitive { + searchCond = "AND m1.text LIKE '%' || ? || '%'" + } else { + searchCond = "AND LOWER(m1.text) LIKE LOWER('%' || ? || '%')" + } + + allFields := db.tableUserMessagesAllFieldsJoin() + + rows, err := db.db.Query( + fmt.Sprintf(` + SELECT + %s, + substr('0000000000000000000000000000000000000000000000000000000000000000' || m1.clock_value, -64, 64) || m1.id as cursor + FROM + user_messages m1 + LEFT JOIN + user_messages m2 + ON + m1.response_to = m2.id + + LEFT JOIN + contacts c + ON + + m1.source = c.id + WHERE + NOT(m1.hide) AND m1.local_chat_id = ? %s + ORDER BY cursor DESC + `, allFields, searchCond), + chatID, searchTerm, + ) + + if err != nil { + return nil, err + } + defer rows.Close() + + var ( + result []*common.Message + ) + for rows.Next() { + var ( + message common.Message + cursor string + ) + if err := db.tableUserMessagesScanAllFields(rows, &message, &cursor); err != nil { + return nil, err + } + result = append(result, &message) + } + + return result, nil +} + // PinnedMessageByChatID returns all pinned messages for a given chatID in descending order. // Ordering is accomplished using two concatenated values: ClockValue and ID. // These two values are also used to compose a cursor which is returned to the result. diff --git a/protocol/messenger.go b/protocol/messenger.go index ce3372a3d..6d57e29b7 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3109,6 +3109,15 @@ func (m *Messenger) MessageByChatID(chatID, cursor string, limit int) ([]*common return m.persistence.MessageByChatID(chatID, cursor, limit) } +func (m *Messenger) AllMessageByChatIDWhichMatchTerm(chatID string, searchTerm string, caseSensitive bool) ([]*common.Message, error) { + _, err := m.persistence.Chat(chatID) + if err != nil { + return nil, err + } + + return m.persistence.AllMessageByChatIDWhichMatchTerm(chatID, searchTerm, caseSensitive) +} + func (m *Messenger) SaveMessages(messages []*common.Message) error { return m.persistence.SaveMessages(messages) } diff --git a/services/ext/api.go b/services/ext/api.go index 916131adf..aaea947a8 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -490,6 +490,17 @@ func (api *PublicAPI) ChatMessages(chatID, cursor string, limit int) (*Applicati }, nil } +func (api *PublicAPI) AllChatMessagesWhichMatchTerm(chatID, searchTerm string, caseSensitive bool) (*ApplicationMessagesResponse, error) { + messages, err := api.service.messenger.AllMessageByChatIDWhichMatchTerm(chatID, searchTerm, caseSensitive) + if err != nil { + return nil, err + } + + return &ApplicationMessagesResponse{ + Messages: messages, + }, nil +} + func (api *PublicAPI) ChatPinnedMessages(chatID, cursor string, limit int) (*ApplicationPinnedMessagesResponse, error) { pinnedMessages, cursor, err := api.service.messenger.PinnedMessageByChatID(chatID, cursor, limit) if err != nil {