A social knowledge tool for researchers built on ATProto

Collection Text Search Feature#

Overview#

Enable text search over a user's collections so they can quickly find and select existing collections when adding cards.

Implementation Options#

Based on our CQRS and DDD architecture patterns, we have several strategic approaches:

Pattern: Enhance GetMyCollectionsUseCase with search capability

Pros:

  • Follows single responsibility - still "getting collections"
  • Reuses existing pagination, sorting, and enrichment logic
  • Maintains consistent API surface
  • Simpler client-side implementation

Implementation:

// Update existing query interface
export interface GetMyCollectionsQuery {
  curatorId: string;
  page?: number;
  limit?: number;
  sortBy?: CollectionSortField;
  sortOrder?: SortOrder;
  searchText?: string; // NEW: Optional search parameter
}

// Update repository interface
export interface ICollectionQueryRepository {
  findByCreator(
    curatorId: string,
    options: CollectionQueryOptions & { searchText?: string }, // Enhanced options
  ): Promise<PaginatedQueryResult<CollectionQueryResultDTO>>;
}

Changes Required:

  • Update GetMyCollectionsQuery interface
  • Update ICollectionQueryRepository.findByCreator() method signature
  • Update DrizzleCollectionQueryRepository implementation with text search logic
  • Update API client types and methods
  • Update HTTP controller to handle search parameter

Option 2: Dedicated Search Use Case#

Pattern: Create separate SearchMyCollectionsUseCase

Pros:

  • Clear separation of concerns
  • Optimized specifically for search scenarios
  • Can implement different search algorithms/ranking
  • Easier to add search-specific features (highlighting, relevance scoring)

Implementation:

// New dedicated use case
export interface SearchMyCollectionsQuery {
  curatorId: string;
  searchText: string; // Required for search
  page?: number;
  limit?: number;
}

export interface SearchMyCollectionsResult {
  collections: CollectionSearchResultDTO[]; // Could include relevance scores
  pagination: PaginationInfo;
  searchMetadata: {
    query: string;
    totalMatches: number;
    searchTime?: number;
  };
}

Changes Required:

  • Create new SearchMyCollectionsUseCase
  • Create new repository method or separate search repository
  • Create new HTTP controller and route
  • Update API client with new search method
  • Implement search-specific DTOs

Option 3: Hybrid Approach#

Pattern: Extend existing use case but add dedicated search endpoint

Implementation:

  • Keep enhanced GetMyCollectionsUseCase for general listing with optional search
  • Add dedicated SearchMyCollectionsUseCase for advanced search features
  • Both share the same underlying repository search capability

Rationale#

  1. Consistency: Aligns with existing patterns where query use cases handle filtering/searching
  2. Simplicity: Single endpoint for both listing and searching collections
  3. Client Efficiency: No need to switch between different API methods
  4. Future-Proof: Easy to add more filter parameters later

Implementation Plan#

1. Domain Layer Updates#

// Update ICollectionQueryRepository interface
export interface CollectionQueryOptions {
  page: number;
  limit: number;
  sortBy: CollectionSortField;
  sortOrder: SortOrder;
  searchText?: string; // NEW
}

2. Application Layer Updates#

// Update GetMyCollectionsQuery
export interface GetMyCollectionsQuery {
  curatorId: string;
  page?: number;
  limit?: number;
  sortBy?: CollectionSortField;
  sortOrder?: SortOrder;
  searchText?: string; // NEW
}

3. Infrastructure Layer Updates#

  • Update DrizzleCollectionQueryRepository.findByCreator() to handle text search
  • Implement SQL text search (LIKE, full-text search, or similar)
  • Update HTTP controller to accept search parameter
  • Update route parameter handling

4. API Client Updates#

// Update existing types
export interface GetMyCollectionsParams {
  page?: number;
  limit?: number;
  sortBy?: string;
  sortOrder?: 'asc' | 'desc';
  searchText?: string; // NEW
}

// No new methods needed - existing getMyCollections() handles search

Search Implementation Details#

Database Search Strategy#

-- Example SQL for text search across name and description
SELECT * FROM collections
WHERE curator_id = ?
  AND (
    name ILIKE '%search_term%'
    OR description ILIKE '%search_term%'
  )
ORDER BY name ASC
LIMIT ? OFFSET ?;

Search Behavior#

  • Empty/null search: Return all collections (existing behavior)
  • Search scope: Collection name and description fields
  • Search type: Case-insensitive partial matching (can be enhanced later)
  • Sorting: Maintain existing sort options, could add relevance sorting later

Migration Path#

  1. Update domain interfaces (backward compatible)
  2. Update use case (backward compatible - search is optional)
  3. Update repository implementation
  4. Update HTTP layer
  5. Update API client types
  6. Update frontend components

This approach provides immediate value while maintaining architectural consistency and leaving room for future enhancements like advanced search features, search analytics, or dedicated search optimization.