# dotselect

Advanced selection with predicates - a user-friendly interface to the `dotpath` engine.

## Overview

`dotselect` provides a curated, user-friendly API for complex path operations. It re-exports the key functions from `dotpath`, making advanced features accessible without requiring knowledge of the underlying engine architecture.

Use `dotselect` when:
- You need predicate filtering (`[?(@['field'] > value)]`)
- You need slice operations (`[1:3]`, `[::2]`)
- You need regex key matching (`~r/pattern/`)
- You want the full power of the dotpath engine with a simple API

## Installation

```bash
pip install dotsuite
```

## Quick Start

```python
from depth.dotselect import find_all, find_first

data = {
    "products": [
        {"name": "Widget", "price": 25, "in_stock": True},
        {"name": "Gadget", "price": 50, "in_stock": False},
        {"name": "Gizmo", "price": 35, "in_stock": True}
    ]
}

# Find all product names
names = find_all("products.*.name", data)
# ["Widget", "Gadget", "Gizmo"]

# Find products in stock
in_stock = find_all("products[?(@['in_stock'] == True)].name", data)
# ["Widget", "Gizmo"]

# Find first expensive product
expensive = find_first("products[?(@['price'] > 30)]", data)
# {"name": "Gadget", "price": 50, "in_stock": False}
```

## When to Use dotselect vs Other Tools

| Tool | Use When |
|------|----------|
| `dotget` | You know the exact path and want a single value |
| `dotstar` | You need simple wildcard matching (`*`) |
| `dotselect` | You need filters, slices, regex, or complex paths |
| `dotpath` | You need to customize the engine or create custom segments |

## Common Patterns

### Filtering by Field Value

```python
# Numeric comparisons
find_all("items[?(@['price'] > 100)]", data)
find_all("items[?(@['quantity'] <= 0)]", data)

# String equality
find_all("users[?(@['role'] == 'admin')]", data)

# Boolean fields
find_all("tasks[?(@['completed'] == True)]", data)
```

### Combining with Extraction

```python
# Filter then extract specific field
find_all("users[?(@['active'] == True)].email", data)

# Filter nested arrays
find_all("orders.*.items[?(@['quantity'] > 1)].name", data)
```

### Slicing Results

```python
# First N items
find_all("items[:5]", data)

# Last N items
find_all("items[-3:]", data)

# Every Nth item
find_all("items[::2]", data)

# Range
find_all("items[2:8]", data)
```

### Deep Searching

```python
# Find all values at any depth matching a key using recursive descent
data = {
    "level1": {
        "target": "found1",
        "level2": {
            "target": "found2"
        }
    }
}

results = find_all("**.target", data)
# ["found1", "found2"]
```

## API Reference

### find_all

```python
def find_all(path: str, doc: Any) -> List[Any]
```

Find all values in a document matching the given path expression.

**Parameters:**
- `path`: Path expression string
- `doc`: Document to search (dict, list, or nested structure)

**Returns:** List of all matching values (empty list if none found)

### find_first

```python
def find_first(path: str, doc: Any) -> Optional[Any]
```

Find the first value in a document matching the given path expression.

**Parameters:**
- `path`: Path expression string
- `doc`: Document to search

**Returns:** First matching value, or `None` if not found

## Path Syntax Reference

| Syntax | Description | Example |
|--------|-------------|---------|
| `key` | Dictionary key | `user.name` |
| `['key']` | Bracket notation | `['special-key']` |
| `[n]` | Array index | `items[0]`, `items[-1]` |
| `[start:stop]` | Slice | `items[1:3]` |
| `[start:stop:step]` | Slice with step | `items[::2]` |
| `*` | Wildcard | `users.*.name` |
| `**` | Recursive descent | `**.name` |
| `[?(expr)]` | Filter | `[?(@['age'] > 18)]` |
| `~r/pat/` | Regex key | `~r/user_\d+/i` |

## Command Line

```bash
# Find all matching values in a JSON file
dotselect "users.*.name" data.json

# Find only the first match
dotselect -f "users[?(@['role'] == 'admin')]" data.json

# Read from stdin (default)
echo '{"items": [1, 2, 3, 4, 5]}' | dotselect "items[1:3]"

# Explicitly read from stdin
cat data.json | dotselect "users.*.email" -

# YAML input (inferred from file extension)
dotselect "servers.*.host" config.yaml

# Force input format
dotselect "items[::2]" data.txt --in yaml
```

### Options

| Flag | Description |
|------|-------------|
| `path_string` | The dotselect path expression (e.g., `users[?(@['active'] == True)].name`). Required. |
| `data_file` | Path to the JSON or YAML data file, or `-` for stdin (default). |
| `-f`, `--first` | Return only the first matching result instead of all results. |
| `--in`, `--input-format` | Format of the input data: `json` or `yaml`. Inferred from file extension if not provided. Defaults to `json` for stdin. |

The result is written to stdout in the same format as the input (JSON or YAML).

## See Also

- [dotget](dotget.md) - Simple exact path access
- [dotstar](dotstar.md) - Wildcard pattern matching
- [dotpath](dotpath.md) - Full engine documentation
