# Query Module

## Overview

The query module provides a unified interface for querying memory snapshot data. It supports various query types and filtering options, enabling flexible analysis of memory usage patterns.

## Components

### 1. QueryEngine

**File**: `src/query/engine.rs`

**Purpose**: Unified query interface for memory data.

**Key Features**:
- Unified: Single interface for all query types
- Efficient: Optimized for fast query execution
- Flexible: Supports filtering and sorting
- Thread-safe: Safe for concurrent access

**Core Implementation**:

```rust
pub struct QueryEngine {
    /// Reference to the snapshot engine
    snapshot_engine: SharedSnapshotEngine,
}

impl QueryEngine {
    /// Create a new QueryEngine
    pub fn new(snapshot_engine: SharedSnapshotEngine) -> Self {
        Self { snapshot_engine }
    }

    /// Get the current snapshot
    fn get_snapshot(&self) -> MemorySnapshot {
        self.snapshot_engine.build_snapshot()
    }

    /// Query for top allocations by size
    pub fn top_allocations(&self, limit: usize) -> QueryResult {
        let snapshot = self.get_snapshot();
        let mut allocations: Vec<_> = snapshot.active_allocations.values().cloned().collect();

        // Sort by size descending
        allocations.sort_by_key(|b| std::cmp::Reverse(b.size));

        // Limit results
        allocations.truncate(limit);

        let total_bytes = allocations.iter().map(|a| a.size).sum();

        QueryResult::Allocations(AllocationQueryResult {
            count: allocations.len(),
            total_bytes,
            allocations,
        })
    }

    /// Query for allocations from a specific thread
    pub fn allocations_by_thread(&self, thread_id: u64) -> QueryResult {
        let snapshot = self.get_snapshot();
        let allocations: Vec<_> = snapshot.active_allocations.values()
            .filter(|a| a.thread_id == thread_id)
            .cloned()
            .collect();

        let total_bytes = allocations.iter().map(|a| a.size).sum();

        QueryResult::Allocations(AllocationQueryResult {
            count: allocations.len(),
            total_bytes,
            allocations,
        })
    }

    /// Query for thread statistics
    pub fn thread_stats(&self) -> QueryResult {
        let snapshot = self.get_snapshot();
        let threads: Vec<_> = snapshot.thread_stats.values().cloned().collect();
        let total_bytes = threads.iter().map(|t| t.total_allocated).sum();

        QueryResult::Threads(ThreadQueryResult {
            count: threads.len(),
            total_bytes,
            threads,
        })
    }

    /// Query for a summary of memory usage
    pub fn summary(&self) -> QueryResult {
        let snapshot = self.get_snapshot();

        QueryResult::Summary(SummaryQueryResult {
            total_allocations: snapshot.stats.total_allocations,
            total_deallocations: snapshot.stats.total_deallocations,
            active_allocations: snapshot.stats.active_allocations,
            total_allocated: snapshot.stats.total_allocated,
            total_deallocated: snapshot.stats.total_deallocated,
            current_memory: snapshot.stats.current_memory,
            peak_memory: snapshot.stats.peak_memory,
            thread_count: snapshot.thread_stats.len(),
        })
    }

    /// Query for allocations with a specific variable name
    pub fn allocations_by_variable(&self, var_name: &str) -> QueryResult {
        let snapshot = self.get_snapshot();
        let allocations: Vec<_> = snapshot.active_allocations.values()
            .filter(|a| a.var_name.as_ref().map(|n| n == var_name).unwrap_or(false))
            .cloned()
            .collect();

        let total_bytes = allocations.iter().map(|a| a.size).sum();

        QueryResult::Allocations(AllocationQueryResult {
            count: allocations.len(),
            total_bytes,
            allocations,
        })
    }

    /// Query for allocations larger than a certain size
    pub fn allocations_larger_than(&self, min_size: usize) -> QueryResult {
        let snapshot = self.get_snapshot();
        let allocations: Vec<_> = snapshot.active_allocations.values()
            .filter(|a| a.size > min_size)
            .cloned()
            .collect();

        let total_bytes = allocations.iter().map(|a| a.size).sum();

        QueryResult::Allocations(AllocationQueryResult {
            count: allocations.len(),
            total_bytes,
            allocations,
        })
    }
}
```

### 2. Query Types

**File**: `src/query/types.rs`

**Purpose**: Type definitions for query results.

**Query Results**:

```rust
pub enum QueryResult {
    /// Allocation query results
    Allocations(AllocationQueryResult),
    /// Thread query results
    Threads(ThreadQueryResult),
    /// Summary query results
    Summary(SummaryQueryResult),
}

pub struct AllocationQueryResult {
    pub count: usize,
    pub total_bytes: usize,
    pub allocations: Vec<ActiveAllocation>,
}

pub struct ThreadQueryResult {
    pub count: usize,
    pub total_bytes: usize,
    pub threads: Vec<ThreadMemoryStats>,
}

pub struct SummaryQueryResult {
    pub total_allocations: usize,
    pub total_deallocations: usize,
    pub active_allocations: usize,
    pub total_allocated: usize,
    pub total_deallocated: usize,
    pub current_memory: usize,
    pub peak_memory: usize,
    pub thread_count: usize,
}
```

## Usage Examples

### Basic Usage

```rust
use memscope::query::QueryEngine;
use memscope::snapshot::SnapshotEngine;
use std::sync::Arc;

// Create query engine
let event_store = Arc::new(EventStore::new());
let snapshot_engine = Arc::new(SnapshotEngine::new(event_store));
let query_engine = QueryEngine::new(snapshot_engine);

// Query summary
let result = query_engine.summary();
match result {
    QueryResult::Summary(summary) => {
        println!("Total allocations: {}", summary.total_allocations);
        println!("Current memory: {} bytes", summary.current_memory);
    }
    _ => {}
}
```

### Query Top Allocations

```rust
// Get top 10 allocations by size
let result = query_engine.top_allocations(10);
match result {
    QueryResult::Allocations(allocations) => {
        println!("Top allocations:");
        for alloc in &allocations.allocations {
            println!("  0x{:x}: {} bytes", alloc.ptr, alloc.size);
        }
    }
    _ => {}
}
```

### Query by Thread

```rust
// Get allocations from thread 1
let result = query_engine.allocations_by_thread(1);
match result {
    QueryResult::Allocations(allocations) => {
        println!("Thread 1 allocations: {}", allocations.count);
    }
    _ => {}
}
```

### Query Large Allocations

```rust
// Find allocations larger than 1MB
let result = query_engine.allocations_larger_than(1024 * 1024);
match result {
    QueryResult::Allocations(allocations) => {
        println!("Large allocations: {}", allocations.count);
    }
    _ => {}
}
```

## Design Principles

### 1. Unified Interface
Single interface for all query types:
- **Benefits**: Consistent API, easier to use
- **Trade-off**: Less flexibility for specific queries

### 2. Type Safety
Strong typing for query results:
- **Benefits**: Compile-time safety, better documentation
- **Trade-off**: More verbose code

### 3. Efficiency
Optimized for fast query execution:
- **Benefits**: Quick analysis
- **Trade-off**: May use more memory for caching

## Best Practices

1. **Query Optimization**: Use specific queries when possible
2. **Result Handling**: Always handle all query result types
3. **Thread Safety**: Use Arc<QueryEngine> for shared access
4. **Error Handling**: Always handle query errors

## Limitations

1. **Snapshot Based**: Queries operate on snapshots, not live data
2. **Memory Usage**: Query results may be large
3. **Performance**: Complex queries may be slow
4. **Filtering**: Limited filtering capabilities

## Future Improvements

1. **Advanced Filtering**: More powerful filtering options
2. **Aggregation Queries**: Group by, sum, avg, etc.
3. **Time-based Queries**: Query by time ranges
4. **Custom Queries**: Allow custom query logic
5. **Query Caching**: Cache query results