feat(search): align time filters
This commit is contained in:
@@ -176,7 +176,7 @@ func (r *SearchRepo) searchConversationsInternal(ctx context.Context, accountID
|
||||
q = applyConversationFilters(q, filter)
|
||||
|
||||
// Apply date range filter
|
||||
q = applyDateRangeFilter(q, filter)
|
||||
q = applyDateRangeFilter(q, filter, "last_activity_at", true)
|
||||
|
||||
// Count
|
||||
if err := q.Count(&total).Error; err != nil {
|
||||
@@ -230,7 +230,7 @@ func (r *SearchRepo) searchMessagesInternal(ctx context.Context, accountID uint,
|
||||
q = applyMessageFilters(q, filter)
|
||||
|
||||
// Apply date range filter
|
||||
q = applyDateRangeFilter(q, filter)
|
||||
q = applyDateRangeFilter(q, filter, "created_at", false)
|
||||
|
||||
// Count
|
||||
if err := q.Count(&total).Error; err != nil {
|
||||
@@ -323,7 +323,7 @@ func (r *SearchRepo) searchContactsInternal(ctx context.Context, accountID uint,
|
||||
q = applyContactFilters(q, filter)
|
||||
|
||||
// Apply date range filter
|
||||
q = applyDateRangeFilter(q, filter)
|
||||
q = applyDateRangeFilter(q, filter, "last_activity_at", true)
|
||||
|
||||
// Count
|
||||
if err := q.Count(&total).Error; err != nil {
|
||||
@@ -363,7 +363,7 @@ func (r *SearchRepo) searchCompaniesInternal(ctx context.Context, accountID uint
|
||||
}
|
||||
}
|
||||
|
||||
q = applyDateRangeFilter(q, filter)
|
||||
q = applyDateRangeFilter(q, filter, "created_at", false)
|
||||
if err := q.Count(&total).Error; err != nil {
|
||||
return nil, 0, fmt.Errorf("search companies count: %w", err)
|
||||
}
|
||||
@@ -408,7 +408,7 @@ func (r *SearchRepo) searchArticlesInternal(ctx context.Context, accountID uint,
|
||||
q = applyArticleFilters(q, filter)
|
||||
|
||||
// Apply date range filter
|
||||
q = applyDateRangeFilter(q, filter)
|
||||
q = applyDateRangeFilter(q, filter, "updated_at", false)
|
||||
|
||||
// Count
|
||||
if err := q.Count(&total).Error; err != nil {
|
||||
@@ -583,18 +583,32 @@ func applyArticleFilters(q *gorm.DB, filter *RepoSearchFilter) *gorm.DB {
|
||||
return q
|
||||
}
|
||||
|
||||
// applyDateRangeFilter applies a date range filter on created_at.
|
||||
func applyDateRangeFilter(q *gorm.DB, filter *RepoSearchFilter) *gorm.DB {
|
||||
// applyDateRangeFilter applies Chatwoot SearchService time filters using the
|
||||
// entity-specific column selected by the caller.
|
||||
func applyDateRangeFilter(q *gorm.DB, filter *RepoSearchFilter, columnName string, unixSeconds bool) *gorm.DB {
|
||||
if filter == nil || !filter.AdvancedSearchEnabled {
|
||||
return q
|
||||
}
|
||||
if columnName == "" {
|
||||
columnName = "created_at"
|
||||
}
|
||||
|
||||
if filter.DateFrom != nil {
|
||||
q = q.Where("created_at >= ?", filter.DateFrom)
|
||||
q = q.Where(columnName+" >= ?", dateRangeValue(filter.DateFrom, unixSeconds))
|
||||
}
|
||||
if filter.DateTo != nil {
|
||||
q = q.Where("created_at <= ?", filter.DateTo)
|
||||
q = q.Where(columnName+" <= ?", dateRangeValue(filter.DateTo, unixSeconds))
|
||||
}
|
||||
|
||||
return q
|
||||
}
|
||||
|
||||
func dateRangeValue(value *time.Time, unixSeconds bool) any {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
if unixSeconds {
|
||||
return value.Unix()
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -350,6 +350,28 @@ func TestSearchRepo_SearchConversations_WithStatusFilter(t *testing.T) {
|
||||
assert.Equal(t, "open", results[0].Status)
|
||||
}
|
||||
|
||||
func TestSearchRepo_SearchConversations_DateFilterUsesLastActivityAt(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
repo := NewSearchRepo(db)
|
||||
|
||||
account := createTestAccountForSearch(t, db)
|
||||
cutoff := time.Date(2026, 6, 7, 12, 0, 0, 0, time.UTC)
|
||||
oldActivity := cutoff.Add(-time.Hour).Unix()
|
||||
recentActivity := cutoff.Add(time.Hour).Unix()
|
||||
oldConv := createTestConversationForSearch(t, db, account.ID, "open", "date-filter")
|
||||
recentConv := createTestConversationForSearch(t, db, account.ID, "open", "date-filter")
|
||||
require.NoError(t, db.Model(oldConv).Update("last_activity_at", oldActivity).Error)
|
||||
require.NoError(t, db.Model(recentConv).Update("last_activity_at", recentActivity).Error)
|
||||
|
||||
filter := &search.SearchFilter{Page: 1, PerPage: 25, AdvancedSearchEnabled: true, DateFrom: &cutoff}
|
||||
results, total, err := repo.SearchConversations(context.Background(), account.ID, "", filter)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), total)
|
||||
require.Len(t, results, 1)
|
||||
assert.Equal(t, recentConv.ID, results[0].ID)
|
||||
}
|
||||
|
||||
// ========== SearchMessages DB tests (PG only for ILIKE) ==========
|
||||
|
||||
func TestSearchRepo_SearchMessages_ContentMatch(t *testing.T) {
|
||||
@@ -463,11 +485,33 @@ func TestSearchRepo_SearchContacts_ResolvedScopeCRMV2(t *testing.T) {
|
||||
assert.Equal(t, lead.ID, results[0].ID)
|
||||
}
|
||||
|
||||
func TestSearchRepo_SearchContacts_DateFilterUsesLastActivityAt(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
repo := NewSearchRepo(db)
|
||||
|
||||
account := createTestAccountForSearch(t, db)
|
||||
cutoff := time.Date(2026, 6, 7, 12, 0, 0, 0, time.UTC)
|
||||
oldActivity := cutoff.Add(-time.Hour).Unix()
|
||||
recentActivity := cutoff.Add(time.Hour).Unix()
|
||||
oldContact := createTestContactForSearch(t, db, account.ID, "Old Activity", "old@example.com", "")
|
||||
recentContact := createTestContactForSearch(t, db, account.ID, "Recent Activity", "recent@example.com", "")
|
||||
require.NoError(t, db.Model(oldContact).Update("last_activity_at", oldActivity).Error)
|
||||
require.NoError(t, db.Model(recentContact).Update("last_activity_at", recentActivity).Error)
|
||||
|
||||
filter := &search.SearchFilter{Page: 1, PerPage: 25, AdvancedSearchEnabled: true, DateFrom: &cutoff}
|
||||
results, total, err := repo.SearchContacts(context.Background(), account.ID, "", filter)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), total)
|
||||
require.Len(t, results, 1)
|
||||
assert.Equal(t, recentContact.ID, results[0].ID)
|
||||
}
|
||||
|
||||
// ========== SearchArticles DB tests (PG only for ILIKE) ==========
|
||||
|
||||
func TestSearchRepo_SearchArticles_TitleMatch(t *testing.T) {
|
||||
skipIfSQLite(t)
|
||||
db := setupTestDB(t)
|
||||
db := setupTestDB(t, &model.Article{})
|
||||
repo := NewSearchRepo(db)
|
||||
|
||||
account := createTestAccountForSearch(t, db)
|
||||
@@ -483,7 +527,7 @@ func TestSearchRepo_SearchArticles_TitleMatch(t *testing.T) {
|
||||
|
||||
func TestSearchRepo_SearchArticles_WithStatusFilter(t *testing.T) {
|
||||
skipIfSQLite(t)
|
||||
db := setupTestDB(t)
|
||||
db := setupTestDB(t, &model.Article{})
|
||||
repo := NewSearchRepo(db)
|
||||
|
||||
account := createTestAccountForSearch(t, db)
|
||||
@@ -499,6 +543,26 @@ func TestSearchRepo_SearchArticles_WithStatusFilter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchRepo_SearchArticles_DateFilterUsesUpdatedAt(t *testing.T) {
|
||||
db := setupTestDB(t, &model.Article{})
|
||||
repo := NewSearchRepo(db)
|
||||
|
||||
account := createTestAccountForSearch(t, db)
|
||||
cutoff := time.Date(2026, 6, 7, 12, 0, 0, 0, time.UTC)
|
||||
oldArticle := createTestArticleForSearch(t, db, account.ID, 1, "Old Updated Article", "desc", "content", "published")
|
||||
recentArticle := createTestArticleForSearch(t, db, account.ID, 1, "Recent Updated Article", "desc", "content", "published")
|
||||
require.NoError(t, db.Model(oldArticle).UpdateColumn("updated_at", cutoff.Add(-time.Hour)).Error)
|
||||
require.NoError(t, db.Model(recentArticle).UpdateColumn("updated_at", cutoff.Add(time.Hour)).Error)
|
||||
|
||||
filter := &search.SearchFilter{Page: 1, PerPage: 25, AdvancedSearchEnabled: true, DateFrom: &cutoff}
|
||||
results, total, err := repo.SearchArticles(context.Background(), account.ID, "", filter)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), total)
|
||||
require.Len(t, results, 1)
|
||||
assert.Equal(t, recentArticle.ID, results[0].ID)
|
||||
}
|
||||
|
||||
// ========== Interface satisfaction test ==========
|
||||
|
||||
func TestSearchRepo_SatisfiesSearchRepoInterface(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user