Skip to main content

MRQL Query Language

MRQL (Mahresources Query Language) is a structured query language for searching across resources, notes, and groups with precise field-level filtering, ordering, and pagination.

When to Use MRQL

Use caseBest tool
Quick keyword searchGlobal search (Ctrl+K)
Filter by one or two fieldsEntity list filters
Complex multi-field conditionsMRQL
Date range + tag + file size combinationsMRQL
Reusable cross-entity queriesMRQL + Saved Queries
Raw SQL with joins and aggregatesSaved Queries (SQL)

Accessing MRQL

Navigate to /mrql in the web UI. The page provides:

  • A syntax-highlighted editor with autocompletion (Ctrl+Space)
  • Real-time validation with inline error markers
  • Run button or Ctrl+Enter to execute
  • Save to persist a query for later reuse
  • Saved Queries panel listing all stored queries
  • Recent Queries history (session-local)

Syntax Reference

Basic Structure

[type = "resource|note|group" AND] <conditions> [ORDER BY <field> [ASC|DESC]] [LIMIT <n>] [OFFSET <n>]

Conditions are field-value comparisons joined with AND, OR, and NOT.

Entity Selector

Use type = "<value>" anywhere in the query to target a specific entity type:

type = resource AND name ~ "photo"
type = note AND tags = "todo"
type = group AND category = "3"

Valid values: resource, note, group.

Omit the type selector entirely to search all entity types at once (cross-entity mode).

Fields

Common fields — available on all entity types:

FieldTypeDescription
idnumberEntity ID
namestringDisplay name
descriptionstringDescription or body text
createddatetimeCreation timestamp
updateddatetimeLast-updated timestamp
tagsrelationAssociated tags (match by name)
meta.<key>string/numberDynamic metadata value

Resource-only fields:

FieldTypeDescription
groups / grouprelationAssociated groups (match by name)
categorystringResource category ID
contentTypestringMIME type (e.g. image/png)
fileSizenumberFile size in bytes (supports kb, mb, gb units)
widthnumberImage/video width in pixels
heightnumberImage/video height in pixels
originalNamestringOriginal filename at upload
hashstringContent hash

Note-only fields:

FieldTypeDescription
groups / grouprelationAssociated groups (match by name)
noteTypestringNote type ID

Group-only fields:

FieldTypeDescription
categorystringGroup category ID
parentrelationParent group (match by name)
childrenrelationChild groups (match by name)

Comparison Operators

OperatorMeaningExample
=Equal (case-insensitive for strings)name = "Report"
!=Not equalcontentType != "application/pdf"
>Greater thanfileSize > 1mb
>=Greater than or equalcreated >= -30d
<Less thanwidth < 800
<=Less than or equalfileSize <= 500kb

String comparisons with = and != are always case-insensitive.

Pattern Matching

The ~ operator performs a contains match by default. Without wildcards, the value is matched anywhere in the field:

contentType ~ "image"     # matches "image/png", "image/jpeg", etc.
name ~ "report" # matches "Q1 Report", "Annual reporting", etc.

Use * for any sequence of characters and ? for a single character to create anchored patterns:

name ~ "project*"         # starts with "project" (no implicit wrapping)
contentType ~ "image/*" # matches "image/png" but not "text/image"
originalName ~ "*.jpg" # ends with .jpg
name ~ "Q?-report" # Q1-report, Q2-report, etc.
Wildcard behavior

When your value contains no * or ? wildcards, ~ automatically wraps it with * on both sides, making it a substring/contains match. As soon as you include any wildcard, the value is used as-is, giving you precise control over anchoring.

The !~ operator is the negated form:

name !~ "draft*"          # does not start with "draft"
contentType !~ "image" # does not contain "image"

Both ~ and !~ are case-insensitive.

Existence Checks

description IS EMPTY          # description is empty string or null
description IS NOT EMPTY # description has a non-empty value
meta.rating IS NULL # meta key not present
meta.rating IS NOT NULL # meta key is present
tags IS EMPTY # no tags associated

Set Operators

contentType IN ("image/png", "image/jpeg", "image/webp")
tags IN ("urgent", "review", "blocked")
contentType NOT IN ("video/mp4", "video/webm")

Search indexed text across the entity's name, description, and content fields:

TEXT ~ "quarterly earnings"
type = note AND TEXT ~ "retrospective action items"

Full-text search uses the database's FTS5 index and supports phrase queries. It is only available when the server is started without -skip-fts.

Boolean Logic

Combine conditions with AND, OR, and NOT. Use parentheses for explicit grouping.

Operator precedence (highest to lowest):

  1. NOT
  2. AND
  3. OR
# AND binds tighter than OR:
type = resource AND (tags = "photo" OR tags = "video")

# NOT applies to the next expression:
type = resource AND NOT tags = "archived"

# Explicit grouping:
(type = resource OR type = note) AND created > -7d

Case Sensitivity

All comparisons are case-insensitive. name = "Report" matches "report", "REPORT", and "Report". Pattern matching with ~ is also case-insensitive.

String Escaping

Strings are double-quoted. Use \" to include a literal quote and \\ for a backslash:

name = "O\"Brien"
originalName ~ "C:\\Users\\*"

Relative Dates

Use relative date literals in datetime comparisons to express time offsets from the current moment:

LiteralMeaning
-7d7 days ago
-2w2 weeks ago
-3m3 months ago
-1y1 year ago
-30d30 days ago
created > -7d                  # created in the last 7 days
updated < -1y # not updated in over a year
created >= -3m AND created <= -1m # created 1-3 months ago

Date Functions

Use built-in functions for date boundaries:

FunctionReturns
NOW()Current timestamp
START_OF_DAY()Midnight of the current day
START_OF_WEEK()Midnight of the current week's Monday
START_OF_MONTH()Midnight of the first day of the current month
START_OF_YEAR()Midnight of January 1 of the current year
created >= START_OF_WEEK()     # created this week
updated < START_OF_MONTH() # not updated this month
created >= START_OF_YEAR() # created this year

File Size Units

Numeric values for fileSize accept unit suffixes (case-insensitive):

SuffixMultiplier
kb1,024 bytes
mb1,048,576 bytes
gb1,073,741,824 bytes
fileSize > 10mb
fileSize < 500kb
fileSize >= 1gb

Ordering and Pagination

ORDER BY <field> [ASC|DESC]
LIMIT <n>
OFFSET <n>

Multiple ORDER BY columns are supported:

type = resource ORDER BY created DESC LIMIT 20
type = note ORDER BY updated ASC, name ASC LIMIT 50 OFFSET 100

The default sort order when ORDER BY is omitted is implementation-defined (typically insertion order).

Traversal

For group entities, you can filter by properties of the parent or children one level deep:

type = group AND parent.name = "Acme Corp"
type = group AND children.name ~ "Q*"
type = group AND parent.category = "1"

Traversal fields follow the same operators as regular fields. Traversal deeper than one level is not supported.

Cross-Entity Queries

Omitting type causes MRQL to fan out the query across resources, notes, and groups simultaneously. Only common fields (id, name, description, created, updated, tags) are valid in cross-entity mode.

name ~ "budget*"                              # search all entity types
tags = "urgent" LIMIT 30 # across all types
TEXT ~ "quarterly review" LIMIT 30 # full-text across all types

Results are returned grouped by entity type (resources, then notes, then groups). ORDER BY, LIMIT, and OFFSET apply globally across the merged result set. Cross-entity sorting supports the common fields: name, created, updated.

Saved Queries

Any query can be saved for later reuse:

  1. Write and run a query in the /mrql editor
  2. Click Save, provide a name and optional description
  3. The query appears in the Saved Queries panel

Saved queries can be:

  • Loaded by clicking them in the panel (populates the editor)
  • Run directly via the CLI with mr mrql run <name-or-id>
  • Deleted by hovering a query and clicking the Delete button
  • Updated via the API (PUT /v1/mrql/saved?id=N)

Examples Cookbook

Finding resources by type and size

type = resource AND contentType ~ "image/*" AND fileSize > 5mb

Recently modified notes with a specific tag

type = note AND tags = "todo" AND updated > -7d ORDER BY updated DESC

Resources added this week without any tags

type = resource AND tags IS EMPTY AND created >= START_OF_WEEK()

Large video files

type = resource AND contentType ~ "video/*" AND fileSize > 500mb ORDER BY fileSize DESC

Groups with no parent (top-level only)

type = group AND parent IS EMPTY

Notes in a specific group updated recently

type = note AND groups = "Project Alpha" AND updated > -30d

Resources matching multiple content types

type = resource AND contentType IN ("image/png", "image/jpeg", "image/webp", "image/gif")

Resources with missing descriptions

type = resource AND description IS EMPTY

Full-text search within a date range

type = note AND TEXT ~ "budget forecast" AND created >= -90d ORDER BY created DESC

Groups in a specific category added this year

type = group AND category = "5" AND created >= START_OF_YEAR()

Resources with metadata rating above threshold

type = resource AND meta.rating > 4

Everything tagged "urgent" across all entity types

tags = "urgent" LIMIT 50

Resources with a specific original filename pattern

type = resource AND originalName ~ "screenshot_*" ORDER BY created DESC

High-resolution images from the last month

type = resource AND contentType ~ "image/*" AND width >= 1920 AND created > -30d

Notes not updated in over six months

type = note AND updated < -180d ORDER BY updated ASC

Groups with children named after a pattern

type = group AND children.name ~ "Q* 2025"

Resources excluding drafts and archived

type = resource AND NOT (tags IN ("draft", "archived")) ORDER BY created DESC LIMIT 25