51 lines
1.4 KiB
Go
51 lines
1.4 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
)
|
|
|
|
// MessageHit is a search result: a message scoped to the member's
|
|
// conversations, with its conversation context.
|
|
type MessageHit struct {
|
|
Message
|
|
ConversationType string
|
|
ConversationName string
|
|
}
|
|
|
|
// SearchMessages finds messages matching query in conversations the user
|
|
// belongs to, newest first. ILIKE substring matching keeps the search
|
|
// dependency-free at family scale; pg_trgm is the documented scaling path.
|
|
func (s *Store) SearchMessages(ctx context.Context, userID int64, query string, limit int) ([]MessageHit, error) {
|
|
if query == "" {
|
|
return nil, nil
|
|
}
|
|
pattern := "%" + query + "%"
|
|
rows, err := s.pool.Query(ctx, `
|
|
SELECT m.id, m.conversation_id, m.sender_id, m.body, m.media_id, m.created_at, m.edited_at, m.deleted_at,
|
|
c.type, c.name
|
|
FROM messages m
|
|
JOIN conversation_members cm ON cm.conversation_id = m.conversation_id AND cm.user_id = $1
|
|
JOIN conversations c ON c.id = m.conversation_id
|
|
WHERE m.body ILIKE $2
|
|
ORDER BY m.id DESC
|
|
LIMIT $3
|
|
`, userID, pattern, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var hits []MessageHit
|
|
for rows.Next() {
|
|
var h MessageHit
|
|
if err := rows.Scan(
|
|
&h.ID, &h.ConversationID, &h.SenderID, &h.Body, &h.MediaID, &h.CreatedAt, &h.EditedAt, &h.DeletedAt,
|
|
&h.ConversationType, &h.ConversationName,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
hits = append(hits, h)
|
|
}
|
|
return hits, nil
|
|
}
|