Memory Metadata Search

basicmachines-co/basic-memory/skills/memory-metadata-search

by basicmachines-cob941460b4d99480fa2eb1a230a62d2847927ea05No license4.1K starsListed Oct 9, 2026Updated Oct 9, 2026Repository updated today

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.

Instructions onlyAI & Agents
AI-generated overview

Guides structured metadata searches in Basic Memory, filtering notes by custom frontmatter fields via search_notes.

What it does
This skill documents how to query notes by structured frontmatter fields instead of free text. It explains the metadata_filters JSON syntax for equality, array-contains, $in, comparison, $between, null, and nested dot-notation filters, plus the tags and status shortcuts and the tag: query prefix. It also lists rules and pitfalls, such as requiring the $ prefix on operators and the fact that metadata filters match Markdown notes only.
When to use it
Use it when finding notes by known field values such as status, priority, type, or confidence, or when narrowing a text search with structured constraints. It also applies to range queries, tag-based filtering, and schema-aware nested field queries.
Requirements
Requires access to the Basic Memory search_notes tool and notes with YAML frontmatter. No scripts or packages are included; it is instructions only.

Memory Metadata Search

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.

When to Use

  • Filtering by status or priority — find all notes with status: draft or priority: high
  • Querying custom fields — any frontmatter key you invent is searchable
  • Range queries — find notes with confidence > 0.7 or score between 0.3 and 0.8
  • Combining text + metadata — narrow a text search with structured constraints
  • Tag-based filtering — find notes tagged with specific frontmatter tags
  • Schema-aware queries — filter by nested schema fields using dot notation

The Tool

All metadata searching uses search_notes. Pass filters via metadata_filters, or use the tags and status convenience shortcuts. Omit query (or pass None) for filter-only searches.

Filter Syntax

Filters are a JSON dictionary. Each key targets a frontmatter field; the value specifies the match condition. Multiple keys combine with AND logic.

Equality

json
{"status": "active"}

Array Contains (all listed values must be present)

json
{"tags": ["security", "oauth"]}

$in (match any value in list)

json
{"priority": {"$in": ["high", "critical"]}}

Comparisons ($gt, $gte, $lt, $lte)

json
{"confidence": {"$gt": 0.7}}

Numeric values use numeric comparison; strings use lexicographic comparison.

$between (inclusive range)

json
{"score": {"$between": [0.3, 0.8]}}

Null (field missing or explicitly null)

json
{"owner": null}

Matches notes with no owner key and notes whose owner is explicitly null. Null works only as a plain equality value — inside $in, $between, an array-contains list, or a comparison it is rejected, because those compare against the value and a comparison with null is never true.

Nested Access (dot notation)

json
{"schema.version": "2"}

Quick Reference

OperatorSyntaxExample
Equality{"field": "value"}{"status": "active"}
Is null{"field": null}{"owner": null}
Array contains{"field": ["a", "b"]}{"tags": ["security", "oauth"]}
$in{"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": [lo, hi]}}{"score": {"$between": [0.3, 0.8]}}
Nested{"a.b": "value"}{"schema.version": "2"}

Rules:

  • Keys must match [A-Za-z0-9_-]+ (dots separate nesting levels)
  • Operator dicts must contain exactly one operator
  • $in and array-contains require non-empty lists
  • $between requires exactly [min, max]
  • null is an is-null match and only valid as a plain equality value
  • Comparison and $between bounds must be finite numbers — a magnitude no float can hold (a 400-digit integer, which JSON keeps as an ordinary int) is refused rather than compared against an infinite bound
  • Metadata filters match Markdown notes only — indexed PDFs, images and other regular files carry no frontmatter and are never hits, not even for null

Warning: Operators MUST include the $ prefix — write $gte, not gte. Without the prefix the filter is treated as an exact-match key and will silently return no results. Correct: {"confidence": {"$gte": 0.7}}. Wrong: {"confidence": {"gte": 0.7}}.

Using search_notes with Metadata

Pass metadata_filters, tags, or status to search_notes. Omit query for filter-only searches, or combine text and filters together.

python
# Filter-only — find all notes with a given statussearch_notes(metadata_filters={"status": "in-progress"})
# Filter-only — high-priority specs in a specific projectsearch_notes(    metadata_filters={"type": "spec", "priority": {"$in": ["high", "critical"]}},    project="research",    page_size=10,)
# Filter-only — notes with confidence above a thresholdsearch_notes(metadata_filters={"confidence": {"$gt": 0.7}})
# Convenience shortcuts for tags and statussearch_notes(status="active")search_notes(tags=["security", "oauth"])
# Text search narrowed by metadatasearch_notes("authentication", metadata_filters={"status": "draft"})
# Mix text, tag shortcut, and advanced filtersearch_notes(    "oauth flow",    tags=["security"],    metadata_filters={"confidence": {"$gt": 0.7}},)

Merging rules: tags and status are convenience shortcuts merged into metadata_filters via setdefault. If the same key exists in metadata_filters, the explicit filter wins.

Tag Search Shorthand

The tag: prefix in a query converts to a tag filter automatically:

python
# These are equivalent:search_notes("tag:tier1")search_notes("", tags=["tier1"])
# Multiple tags (comma or space separated) — all must match:search_notes("tag:tier1,alpha")

Example: Custom Frontmatter in Practice

A note with custom fields:

markdown
---title: Auth Designtype: spectags: [security, oauth]status: in-progresspriority: highconfidence: 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]]

Queries that find it:

python
# By status and typesearch_notes(metadata_filters={"status": "in-progress", "type": "spec"})
# By numeric thresholdsearch_notes(metadata_filters={"confidence": {"$gt": 0.7}})
# By priority setsearch_notes(metadata_filters={"priority": {"$in": ["high", "critical"]}})
# By tag shorthandsearch_notes("tag:security")
# Combined text + metadatasearch_notes("OAuth", metadata_filters={"status": "in-progress"})

Guidelines

  • Use metadata search 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.
  • Use text search for content queries. If you're looking for notes about something, text search is better. Combine both when you need precision.
  • Custom fields are free. Any YAML key you put in frontmatter becomes queryable — no schema or configuration required.
  • Multiple filters are AND. {"status": "active", "priority": "high"} requires both conditions.
  • Omit query for filter-only searches. search_notes(metadata_filters={"status": "active"}) works without a text query.
  • Dot notation for nesting. Access nested YAML structures with dots: {"schema.version": "2"} queries the version key inside a schema object.
  • Tags shortcut is convenient but limited. tags and status are sugar for common fields. For anything else, use metadata_filters directly.

Source and attribution

Source:basicmachines-co/basic-memoryinskills/memory-metadata-searchat commitb941460

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal