|
| 1 | +--- |
| 2 | +name: memory-metadata-search |
| 3 | +description: "Structured metadata search for Basic Memory: query notes by custom frontmatter fields using equality, range, array, and nested filters. Use when finding notes by status, priority, confidence, or any custom YAML field rather than free-text content." |
| 4 | +--- |
| 5 | + |
| 6 | +# Memory Metadata Search |
| 7 | + |
| 8 | +Find notes by their structured frontmatter fields instead of (or in addition to) free-text content. Any custom YAML key in a note's frontmatter beyond the standard set (`title`, `type`, `tags`, `permalink`, `schema`) is automatically indexed as `entity_metadata` and becomes queryable. |
| 9 | + |
| 10 | +## When to Use |
| 11 | + |
| 12 | +- **Filtering by status or priority** — find all notes with `status: draft` or `priority: high` |
| 13 | +- **Querying custom fields** — any frontmatter key you invent is searchable |
| 14 | +- **Range queries** — find notes with `confidence > 0.7` or `score between 0.3 and 0.8` |
| 15 | +- **Combining text + metadata** — narrow a text search with structured constraints |
| 16 | +- **Tag-based filtering** — find notes tagged with specific frontmatter tags |
| 17 | +- **Schema-aware queries** — filter by nested schema fields using dot notation |
| 18 | + |
| 19 | +## Tool |
| 20 | + |
| 21 | +Use the `bm_search` tool with the optional `metadata_filters`, `tags`, and `status` parameters. |
| 22 | + |
| 23 | +| Parameter | Type | Description | |
| 24 | +|-----------|------|-------------| |
| 25 | +| `query` | string | Text search query (can be empty for filter-only searches) | |
| 26 | +| `metadata_filters` | object | Filter by frontmatter fields (see filter syntax below) | |
| 27 | +| `tags` | string[] | Filter by frontmatter tags (all must match) | |
| 28 | +| `status` | string | Filter by frontmatter status field | |
| 29 | + |
| 30 | +## Filter Syntax |
| 31 | + |
| 32 | +Filters are a JSON dictionary. Each key targets a frontmatter field; the value specifies the match condition. Multiple keys combine with **AND** logic. |
| 33 | + |
| 34 | +### Equality |
| 35 | + |
| 36 | +```json |
| 37 | +{"status": "active"} |
| 38 | +``` |
| 39 | + |
| 40 | +### Array Contains (all listed values must be present) |
| 41 | + |
| 42 | +```json |
| 43 | +{"tags": ["security", "oauth"]} |
| 44 | +``` |
| 45 | + |
| 46 | +### `$in` (match any value in list) |
| 47 | + |
| 48 | +```json |
| 49 | +{"priority": {"$in": ["high", "critical"]}} |
| 50 | +``` |
| 51 | + |
| 52 | +### Comparisons (`$gt`, `$gte`, `$lt`, `$lte`) |
| 53 | + |
| 54 | +```json |
| 55 | +{"confidence": {"$gt": 0.7}} |
| 56 | +``` |
| 57 | + |
| 58 | +Numeric values use numeric comparison; strings use lexicographic comparison. |
| 59 | + |
| 60 | +### `$between` (inclusive range) |
| 61 | + |
| 62 | +```json |
| 63 | +{"score": {"$between": [0.3, 0.8]}} |
| 64 | +``` |
| 65 | + |
| 66 | +### Nested Access (dot notation) |
| 67 | + |
| 68 | +```json |
| 69 | +{"schema.version": "2"} |
| 70 | +``` |
| 71 | + |
| 72 | +### Quick Reference |
| 73 | + |
| 74 | +| Operator | Syntax | Example | |
| 75 | +|----------|--------|---------| |
| 76 | +| Equality | `{"field": "value"}` | `{"status": "active"}` | |
| 77 | +| Array contains | `{"field": ["a", "b"]}` | `{"tags": ["security", "oauth"]}` | |
| 78 | +| `$in` | `{"field": {"$in": [...]}}` | `{"priority": {"$in": ["high", "critical"]}}` | |
| 79 | +| `$gt` / `$gte` | `{"field": {"$gt": N}}` | `{"confidence": {"$gt": 0.7}}` | |
| 80 | +| `$lt` / `$lte` | `{"field": {"$lt": N}}` | `{"score": {"$lt": 0.5}}` | |
| 81 | +| `$between` | `{"field": {"$between": [lo, hi]}}` | `{"score": {"$between": [0.3, 0.8]}}` | |
| 82 | +| Nested | `{"a.b": "value"}` | `{"schema.version": "2"}` | |
| 83 | + |
| 84 | +**Rules:** |
| 85 | +- Keys must match `[A-Za-z0-9_-]+` (dots separate nesting levels) |
| 86 | +- Operator dicts must contain exactly one operator |
| 87 | +- `$in` and array-contains require non-empty lists |
| 88 | +- `$between` requires exactly `[min, max]` |
| 89 | + |
| 90 | +## Examples |
| 91 | + |
| 92 | +### Metadata-only search (empty query) |
| 93 | + |
| 94 | +``` |
| 95 | +bm_search(query="", metadata_filters={"status": "in-progress", "type": "spec"}) |
| 96 | +``` |
| 97 | + |
| 98 | +### Text search narrowed by metadata |
| 99 | + |
| 100 | +``` |
| 101 | +bm_search(query="authentication", metadata_filters={"status": "draft"}) |
| 102 | +``` |
| 103 | + |
| 104 | +### Filter by tags |
| 105 | + |
| 106 | +``` |
| 107 | +bm_search(query="", tags=["security", "oauth"]) |
| 108 | +``` |
| 109 | + |
| 110 | +### Filter by status shortcut |
| 111 | + |
| 112 | +``` |
| 113 | +bm_search(query="planning", status="active") |
| 114 | +``` |
| 115 | + |
| 116 | +### Combined text + metadata + tags |
| 117 | + |
| 118 | +``` |
| 119 | +bm_search( |
| 120 | + query="oauth flow", |
| 121 | + metadata_filters={"confidence": {"$gt": 0.7}}, |
| 122 | + tags=["security"], |
| 123 | + status="in-progress", |
| 124 | +) |
| 125 | +``` |
| 126 | + |
| 127 | +### High-priority notes in a specific project |
| 128 | + |
| 129 | +``` |
| 130 | +bm_search( |
| 131 | + query="", |
| 132 | + metadata_filters={"priority": {"$in": ["high", "critical"]}}, |
| 133 | + project="research", |
| 134 | + limit=10, |
| 135 | +) |
| 136 | +``` |
| 137 | + |
| 138 | +### Numeric range query |
| 139 | + |
| 140 | +``` |
| 141 | +bm_search(query="", metadata_filters={"score": {"$between": [0.3, 0.8]}}) |
| 142 | +``` |
| 143 | + |
| 144 | +## Tag Search Shorthand |
| 145 | + |
| 146 | +The `tag:` prefix in a query converts to a tag filter automatically: |
| 147 | + |
| 148 | +``` |
| 149 | +# These are equivalent: |
| 150 | +bm_search(query="tag:tier1") |
| 151 | +bm_search(query="", tags=["tier1"]) |
| 152 | +
|
| 153 | +# Multiple tags (comma or space separated) — all must match: |
| 154 | +bm_search(query="tag:tier1,alpha") |
| 155 | +``` |
| 156 | + |
| 157 | +## Example: Custom Frontmatter in Practice |
| 158 | + |
| 159 | +A note with custom fields: |
| 160 | + |
| 161 | +```markdown |
| 162 | +--- |
| 163 | +title: Auth Design |
| 164 | +type: spec |
| 165 | +tags: [security, oauth] |
| 166 | +status: in-progress |
| 167 | +priority: high |
| 168 | +confidence: 0.85 |
| 169 | +--- |
| 170 | + |
| 171 | +# Auth Design |
| 172 | + |
| 173 | +## Observations |
| 174 | +- [decision] Use OAuth 2.1 with PKCE for all client types #security |
| 175 | +- [requirement] Token refresh must be transparent to the user |
| 176 | + |
| 177 | +## Relations |
| 178 | +- implements [[Security Requirements]] |
| 179 | +``` |
| 180 | + |
| 181 | +Queries that find it: |
| 182 | + |
| 183 | +``` |
| 184 | +# By status and type |
| 185 | +bm_search(query="", metadata_filters={"status": "in-progress", "type": "spec"}) |
| 186 | +
|
| 187 | +# By numeric threshold |
| 188 | +bm_search(query="", metadata_filters={"confidence": {"$gt": 0.7}}) |
| 189 | +
|
| 190 | +# By priority set |
| 191 | +bm_search(query="", metadata_filters={"priority": {"$in": ["high", "critical"]}}) |
| 192 | +
|
| 193 | +# By tag shorthand |
| 194 | +bm_search(query="tag:security") |
| 195 | +
|
| 196 | +# Combined text + metadata |
| 197 | +bm_search(query="OAuth", metadata_filters={"status": "in-progress"}) |
| 198 | +``` |
| 199 | + |
| 200 | +## Guidelines |
| 201 | + |
| 202 | +- **Use metadata filters for structured queries.** If you're looking for notes by a known field value (status, priority, type), metadata filters are more precise than text search. |
| 203 | +- **Use text search for content queries.** If you're looking for notes *about* something, text search is better. Combine both when you need precision. |
| 204 | +- **Custom fields are free.** Any YAML key you put in frontmatter becomes queryable — no schema or configuration required. |
| 205 | +- **Multiple filters are AND.** `{"status": "active", "priority": "high"}` requires both conditions. |
| 206 | +- **Use empty query for filter-only searches.** Pass `query=""` with `metadata_filters` when you only need structured filtering. |
| 207 | +- **Dot notation for nesting.** Access nested YAML structures with dots: `{"schema.version": "2"}` queries the `version` key inside a `schema` object. |
| 208 | +- **Tags and status are convenient shortcuts.** Use the dedicated `tags` and `status` parameters for common fields, or `metadata_filters` for anything else. |
0 commit comments