mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
73413486bc
Make `query` optional in `search_notes` so it becomes the single search tool. Remove `search_by_metadata` entirely — it was unreleased and redundant since `search_notes` already supports `metadata_filters`, `tags`, and `status`. - 🔧 `query` param is now `Optional[str] = None` - 🛡️ Added None guards for project detection and URL resolution - ✅ Added `no_criteria()` validation with helpful error message - 🗑️ Deleted `search_by_metadata` tool, imports, tests, and contract entry - 📝 Updated docs, README, and v0.19.0 release notes Closes #605 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
261 lines
7.1 KiB
Markdown
261 lines
7.1 KiB
Markdown
# Metadata Search Reference
|
|
|
|
Basic Memory automatically indexes custom frontmatter fields so you can query them with structured filters. Any YAML key in a note's frontmatter beyond the standard set (`title`, `type`, `tags`, `permalink`, `schema`) is stored as `entity_metadata` and becomes searchable.
|
|
|
|
## Querying with `search_notes`
|
|
|
|
`search_notes` is the single search tool for all queries — text, metadata filters, or both. The `query` parameter is optional, so you can use metadata filters alone without passing an empty string.
|
|
|
|
## Filter Syntax
|
|
|
|
Filters are a JSON dictionary where each key targets a frontmatter field and the value specifies the match condition. Multiple keys combine with **AND** logic — every filter must match.
|
|
|
|
### Equality
|
|
|
|
Match a single value exactly.
|
|
|
|
```json
|
|
{"status": "active"}
|
|
```
|
|
|
|
Finds notes whose frontmatter contains `status: active`.
|
|
|
|
### Array Contains (all)
|
|
|
|
Pass a list to require **all** listed values to be present in the field.
|
|
|
|
```json
|
|
{"tags": ["security", "oauth"]}
|
|
```
|
|
|
|
Finds notes tagged with both `security` and `oauth`.
|
|
|
|
### `$in` (any of)
|
|
|
|
Match if the field equals **any** value in the list.
|
|
|
|
```json
|
|
{"priority": {"$in": ["high", "critical"]}}
|
|
```
|
|
|
|
### `$gt`, `$gte`, `$lt`, `$lte`
|
|
|
|
Numeric and text comparisons. Numeric values use numeric comparison; strings use lexicographic comparison.
|
|
|
|
```json
|
|
{"confidence": {"$gt": 0.7}}
|
|
{"score": {"$lte": 100}}
|
|
```
|
|
|
|
### `$between`
|
|
|
|
Range filter (inclusive). Takes a `[min, max]` pair.
|
|
|
|
```json
|
|
{"score": {"$between": [0.3, 0.8]}}
|
|
```
|
|
|
|
### Nested Access (dot notation)
|
|
|
|
Access nested frontmatter values using dots.
|
|
|
|
```json
|
|
{"schema.version": "2"}
|
|
```
|
|
|
|
This queries the `version` key inside a `schema` object in frontmatter.
|
|
|
|
### Summary Table
|
|
|
|
| Operator | Syntax | Example |
|
|
|----------|--------|---------|
|
|
| Equality | `{"field": "value"}` | `{"status": "active"}` |
|
|
| Array contains (all) | `{"field": ["a", "b"]}` | `{"tags": ["security", "oauth"]}` |
|
|
| `$in` (any of) | `{"field": {"$in": [...]}}` | `{"priority": {"$in": ["high", "critical"]}}` |
|
|
| `$gt` / `$gte` | `{"field": {"$gt": N}}` | `{"confidence": {"$gt": 0.7}}` |
|
|
| `$lt` / `$lte` | `{"field": {"$lt": N}}` | `{"score": {"$lt": 0.5}}` |
|
|
| `$between` | `{"field": {"$between": [min, max]}}` | `{"score": {"$between": [0.3, 0.8]}}` |
|
|
| Nested access | `{"a.b": "value"}` | `{"schema.version": "2"}` |
|
|
|
|
**Key rules:**
|
|
- Filter keys must match `[A-Za-z0-9_-]+` (dots separate nesting levels).
|
|
- Each operator dict must contain exactly one operator.
|
|
- `$in` and array-contains require non-empty lists.
|
|
- `$between` requires exactly two values `[min, max]`.
|
|
|
|
## MCP Tool — `search_notes`
|
|
|
|
`search_notes` is the single search tool for text queries, metadata filters, or both. The `query` parameter is optional.
|
|
|
|
**Relevant parameters:**
|
|
|
|
| Parameter | Type | Description |
|
|
|-----------|------|-------------|
|
|
| `query` | string (optional) | Text search query. Omit for filter-only searches. |
|
|
| `metadata_filters` | dict | Structured filter dict (see syntax above) |
|
|
| `tags` | list[str] | Convenience shorthand — merged into `metadata_filters["tags"]` |
|
|
| `status` | string | Convenience shorthand — merged into `metadata_filters["status"]` |
|
|
|
|
**Merging rules:** `tags` and `status` are convenience shortcuts. They are merged into `metadata_filters` using `setdefault` — if the same key already exists in `metadata_filters`, the explicit filter wins.
|
|
|
|
**Examples:**
|
|
|
|
```python
|
|
# Text search filtered by metadata
|
|
await search_notes("authentication", metadata_filters={"status": "draft"})
|
|
|
|
# Filter-only search (no query needed)
|
|
await search_notes(metadata_filters={"type": "spec"})
|
|
|
|
# Combine text, tags shortcut, and metadata
|
|
await search_notes(
|
|
"oauth flow",
|
|
tags=["security"],
|
|
metadata_filters={"confidence": {"$gt": 0.7}},
|
|
)
|
|
|
|
# Convenience shortcuts
|
|
await search_notes("planning", status="active")
|
|
await search_notes(tags=["tier1", "alpha"])
|
|
```
|
|
|
|
## Tag Search Shortcuts
|
|
|
|
The `tag:` prefix in a search query is a shorthand for tag-based metadata filtering. When `search_notes` receives a query starting with `tag:`, it converts the query into a `tags` filter and clears the text query.
|
|
|
|
```python
|
|
# These are equivalent:
|
|
await search_notes("tag:tier1")
|
|
await search_notes("", tags=["tier1"])
|
|
|
|
# Multiple tags (comma or space separated) — all must be present:
|
|
await search_notes("tag:tier1,alpha")
|
|
await search_notes("tag:tier1 alpha")
|
|
```
|
|
|
|
## CLI Access
|
|
|
|
The `bm tool search-notes` command exposes metadata filtering via `--meta` and `--filter` flags.
|
|
|
|
### `--meta` — simple key=value filters
|
|
|
|
Repeatable flag for equality filters on frontmatter fields.
|
|
|
|
```bash
|
|
# Single filter
|
|
bm tool search-notes "my query" --meta status=draft
|
|
|
|
# Multiple filters (AND logic)
|
|
bm tool search-notes "" --meta status=active --meta priority=high
|
|
```
|
|
|
|
### `--filter` — advanced JSON filters
|
|
|
|
Pass a full JSON filter dictionary for operator-based queries.
|
|
|
|
```bash
|
|
# Range filter
|
|
bm tool search-notes "" --filter '{"score": {"$between": [0.3, 0.8]}}'
|
|
|
|
# $in filter
|
|
bm tool search-notes "" --filter '{"priority": {"$in": ["high", "critical"]}}'
|
|
```
|
|
|
|
### `--tag` and `--status` — convenience shortcuts
|
|
|
|
```bash
|
|
bm tool search-notes "query" --tag security --tag oauth
|
|
bm tool search-notes "" --status draft
|
|
```
|
|
|
|
### Combined example
|
|
|
|
```bash
|
|
bm tool search-notes "authentication" --tag security --meta status=draft --type spec
|
|
```
|
|
|
|
## Practical Examples
|
|
|
|
### Example notes with custom frontmatter
|
|
|
|
**`specs/auth-design.md`:**
|
|
|
|
```markdown
|
|
---
|
|
title: Auth Design
|
|
type: spec
|
|
tags: [security, oauth]
|
|
status: in-progress
|
|
priority: high
|
|
confidence: 0.85
|
|
---
|
|
|
|
# Auth Design
|
|
|
|
## Observations
|
|
- [decision] Use OAuth 2.1 with PKCE for all client types #security
|
|
- [requirement] Token refresh must be transparent to the user
|
|
|
|
## Relations
|
|
- implements [[Security Requirements]]
|
|
```
|
|
|
|
**`specs/search-redesign.md`:**
|
|
|
|
```markdown
|
|
---
|
|
title: Search Redesign
|
|
type: spec
|
|
tags: [search, performance]
|
|
status: draft
|
|
priority: medium
|
|
confidence: 0.6
|
|
---
|
|
|
|
# Search Redesign
|
|
|
|
## Observations
|
|
- [goal] Sub-100ms search response times #performance
|
|
- [approach] Hybrid FTS + vector retrieval
|
|
|
|
## Relations
|
|
- depends_on [[Database Schema]]
|
|
```
|
|
|
|
### Queries that find them
|
|
|
|
```python
|
|
# Find all in-progress specs
|
|
await search_notes(metadata_filters={"status": "in-progress", "type": "spec"})
|
|
# → Auth Design
|
|
|
|
# Find high-confidence specs
|
|
await search_notes(metadata_filters={"confidence": {"$gt": 0.7}})
|
|
# → Auth Design (confidence: 0.85)
|
|
|
|
# Find specs with priority high or medium
|
|
await search_notes(metadata_filters={"priority": {"$in": ["high", "medium"]}})
|
|
# → Auth Design, Search Redesign
|
|
|
|
# Find specs in a confidence range
|
|
await search_notes(metadata_filters={"confidence": {"$between": [0.5, 0.9]}})
|
|
# → Auth Design (0.85), Search Redesign (0.6)
|
|
|
|
# Find notes tagged with security
|
|
await search_notes("tag:security")
|
|
# → Auth Design
|
|
|
|
# Combined: text search + metadata filter
|
|
await search_notes("OAuth", metadata_filters={"status": "in-progress"})
|
|
# → Auth Design
|
|
```
|
|
|
|
### CLI equivalents
|
|
|
|
```bash
|
|
bm tool search-notes "" --meta status=in-progress --type spec
|
|
bm tool search-notes "" --filter '{"confidence": {"$gt": 0.7}}'
|
|
bm tool search-notes "OAuth" --meta status=in-progress
|
|
bm tool search-notes --tag security
|
|
```
|