Skip to content

Scout Search - Google-Style Interface

Overview

A unified search interface for Scout that provides instant access to messages, rooms, users, shared objects, and campaign data through a single search box.

Design Inspiration

┌─────────────────────────────────────────────────────────────────┐
│  🔍  Search Scout...                                      ⌘K   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  RECENT                                                         │
│  ├─ 📝 "voter outreach strategy" in #campaign-team             │
│  ├─ 👤 Jane Smith                                               │
│  └─ 📊 Segment: High-propensity voters                         │
│                                                                 │
│  SUGGESTED                                                      │
│  ├─ 🔍 Search messages...                                       │
│  ├─ 👥 Find users...                                            │
│  ├─ 📁 Browse files...                                          │
│  └─ ❓ Ask Scout AI...                                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Search Categories

1. Messages

in:messages "voter turnout"           # Full-text message search
in:messages from:jane "budget"        # Messages from user
in:messages in:#campaign-team         # Messages in room
in:messages has:file                  # Messages with attachments
in:messages after:2024-01-01          # Date filtering

2. Rooms

in:rooms campaign                     # Room name search
in:rooms type:dm                      # DMs only
in:rooms type:group                   # Groups only
in:rooms with:jane                    # Rooms with user

3. Users

in:users john                         # Name search
in:users role:admin                   # By role
in:users online                       # Currently online

4. Shared Objects

in:files report                       # File search
in:maps district                      # Map search
in:queries "score > 80"               # Saved queries
in:segments high-priority             # Segments

5. Campaign Data (via AI)

/scout how many voters in district 5?
/scout show me recent imports
/scout summarize #campaign-team

UI Components

Search Bar (Global)

Always accessible via ⌘K / Ctrl+K:

<div class="scout-search-overlay">
  <div class="scout-search-modal">
    <input
      type="text"
      placeholder="Search Scout..."
      class="scout-search-input"
      autofocus
    />
    <div class="scout-search-results">
      <!-- Dynamic results -->
    </div>
    <div class="scout-search-footer">
      <span>↵ to select</span>
      <span>↑↓ to navigate</span>
      <span>esc to close</span>
    </div>
  </div>
</div>

Result Types

<!-- Message Result -->
<div class="search-result search-result-message">
  <div class="result-icon">💬</div>
  <div class="result-content">
    <div class="result-title">
      <span class="result-author">Jane Smith</span>
      <span class="result-room">in #campaign-team</span>
    </div>
    <div class="result-snippet">
      ...discussing <mark>voter outreach</mark> strategy for Q2...
    </div>
    <div class="result-meta">Jan 12, 2024</div>
  </div>
</div>

<!-- User Result -->
<div class="search-result search-result-user">
  <div class="result-icon">
    <img src="/avatar/jane" class="avatar-sm" />
  </div>
  <div class="result-content">
    <div class="result-title">Jane Smith</div>
    <div class="result-meta">Field Director • Online</div>
  </div>
  <div class="result-actions">
    <button>Message</button>
  </div>
</div>

<!-- File Result -->
<div class="search-result search-result-file">
  <div class="result-icon">📄</div>
  <div class="result-content">
    <div class="result-title">Q4 Voter Analysis.pdf</div>
    <div class="result-meta">Shared by John • 2.4 MB • Dec 15</div>
  </div>
</div>

Backend API

Search Endpoint

POST /api/scout/search
{
  "query": "voter outreach",
  "categories": ["messages", "files"],  // Optional filter
  "limit": 20,
  "offset": 0,
  "filters": {
    "room_id": "uuid",        // Optional
    "user_id": "uuid",        // Optional
    "date_from": "2024-01-01",
    "date_to": "2024-01-31",
    "has_object": true
  }
}

Response:
{
  "results": [
    {
      "type": "message",
      "id": "msg-uuid",
      "score": 0.95,
      "snippet": "...discussing <em>voter outreach</em>...",
      "data": { /* full message */ }
    },
    {
      "type": "file",
      "id": "obj-uuid",
      "score": 0.82,
      "data": { /* file object */ }
    }
  ],
  "total": 47,
  "categories": {
    "messages": 32,
    "files": 10,
    "users": 3,
    "rooms": 2
  }
}

Suggestions Endpoint

GET /api/scout/search/suggest?q=vot

Response:
{
  "suggestions": [
    {"text": "voter outreach", "type": "recent"},
    {"text": "voters in district 5", "type": "query"},
    {"text": "Voter Analysis.pdf", "type": "file"}
  ]
}

Full-Text Search Implementation

Option A: DuckDB FTS (Simple)

-- Create FTS index on messages
CREATE INDEX idx_scout_message_fts ON scout_message
USING fts(content);

-- Search query
SELECT * FROM scout_message
WHERE content MATCH 'voter outreach'
ORDER BY fts_score DESC
LIMIT 20;

Option B: Dedicated Search (Better)

If search becomes heavy, add a search service:

# Using tantivy (Rust) or meilisearch
class ScoutSearchService:
    def index_message(self, message: ScoutMessage):
        self.index.add_document({
            "id": message.id,
            "type": "message",
            "content": message.content,
            "room_id": message.room_id,
            "sender_id": message.sender_id,
            "created_at": message.created_at,
            "object_type": message.object.type if message.object else None
        })

    def search(self, query: str, filters: dict) -> list[SearchResult]:
        return self.index.search(query, filters)

Search Syntax Parser

class SearchQueryParser:
    """Parse Google-style search syntax."""

    OPERATORS = {
        "in:": "category",      # in:messages, in:files
        "from:": "sender",      # from:jane
        "to:": "recipient",     # to:john
        "has:": "has_object",   # has:file, has:map
        "after:": "date_from",
        "before:": "date_to",
        "is:": "status",        # is:unread, is:pinned
    }

    def parse(self, query: str) -> SearchQuery:
        tokens = self.tokenize(query)
        filters = {}
        text_parts = []

        for token in tokens:
            if ":" in token:
                op, value = token.split(":", 1)
                if op + ":" in self.OPERATORS:
                    filters[self.OPERATORS[op + ":"]] = value
                    continue
            text_parts.append(token)

        return SearchQuery(
            text=" ".join(text_parts),
            filters=filters
        )

# Example
parser = SearchQueryParser()
q = parser.parse('in:messages from:jane "voter outreach"')
# SearchQuery(text="voter outreach", filters={"category": "messages", "sender": "jane"})

Keyboard Navigation

Key Action
⌘K / Ctrl+K Open search
Esc Close search
/ Navigate results
Enter Select result
Tab Cycle categories
⌘Enter Open in new tab

Search Result Actions

Result Type Primary Action Secondary Actions
Message Jump to message Copy link, Reply
User Open DM View profile
Room Open room Room settings
File Preview/Download Share, Copy link
Query Execute Edit, Save

Implementation Phases

Phase 1: Basic Search (2 hours)

  • Search API endpoint
  • DuckDB full-text on messages
  • Basic UI with keyboard shortcut
  • Message results only

Phase 2: Multi-Category (2 hours)

  • Add rooms, users, files search
  • Category tabs/filters
  • Result type renderers

Phase 3: Advanced Syntax (1 hour)

  • Query parser for operators
  • Date range filters
  • Object type filters

Phase 4: Suggestions (1 hour)

  • Recent searches
  • Typeahead suggestions
  • Popular queries

Phase 5: AI Integration (2 hours)

  • /scout queries from search
  • Natural language to structured query
  • "Ask AI" result type

Database Schema Additions

-- Search history for suggestions
CREATE TABLE IF NOT EXISTS scout_search_history (
    id VARCHAR PRIMARY KEY,
    user_id VARCHAR NOT NULL,
    query VARCHAR NOT NULL,
    category VARCHAR,
    result_count INTEGER,
    selected_result_id VARCHAR,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_scout_search_user ON scout_search_history(user_id, created_at DESC);

-- Full-text index (if not using external search)
-- DuckDB FTS extension required
INSTALL fts;
LOAD fts;

PRAGMA create_fts_index('scout_message', 'id', 'content');

  • Command Palette: ⌘K can also show commands (like VS Code)
  • Quick Switcher: Fast room switching with ⌘K then type room name
  • AI Chat: Natural language queries via /scout

Open Questions

  1. Scope: Search across all rooms or respect room membership?
  2. Privacy: Can admins search all messages?
  3. Indexing: Real-time or batch indexing?
  4. External: Include campaign data (contacts, events) in search?