Query language
Search accepts either a plain term or a query in a small language over projects and versions. This is the language.
repo == releases and scope == dev.kingtux and version ~= "1.*"name ~= *.jar and not release_type == Snapshot(scope == dev.kingtux or scope == com.example) and created > 2024-01-01It lives in its own crate with no dependency on the web framework or the database driver, so it can be lifted out and used elsewhere.
A query is a boolean expression over comparisons:
field <operator> valueComparisons combine with and, or, not and parentheses. Two comparisons side by side mean
and, so these are the same query:
scope == dev.kingtux and version ~= 1.*scope == dev.kingtux version ~= 1.*Fields
Section titled “Fields”| Field | Aliases | What |
|---|---|---|
repository |
repo |
The repository’s name. |
storage |
The storage it lives in. | |
project |
project_key, key |
The full key: dev.kingtux:tms, @nitro/example. |
scope |
group, groupid |
A Maven groupId, an npm scope. |
name |
artifact, artifactid |
The project’s name, without its scope. |
description |
The project’s description. | |
version |
A version string. | |
release_type |
releasetype, type |
Stable, Snapshot, Alpha, Beta, ReleaseCandidate, Unknown. |
created |
created_at |
When a version was first published. |
updated |
updated_at |
When a version was last updated. |
Field names are case-insensitive, and the aliases exist because the same concept is called different things by different ecosystems — a Maven user should not have to know that this codebase settled on “scope”.
GET /api/search/fields returns the list, so a client can offer them rather than hardcoding a
second copy.
Operators
Section titled “Operators”| Operator | Meaning | Applies to |
|---|---|---|
== |
Equals | Any field |
!= |
Does not equal | Any field |
~= |
Glob match | Any field |
!~ |
Does not match the glob | Any field |
> |
After | created, updated |
>= |
At or after | created, updated |
< |
Before | created, updated |
<= |
At or before | created, updated |
Text comparisons are case-insensitive, matching how coordinates are treated everywhere else.
Ordering comparisons are rejected on non-temporal fields. name > b would compile to a
lexicographic comparison, which is almost never what anyone means and never says so. You get a
parse error instead.
~= and !~ take a glob:
*— any run of characters, including none.?— exactly one character.
name ~= *-api every artifact whose name ends in -apiversion ~= 1.* every 1.x versionproject ~= @nitro/* every package in the @nitro scopeValues
Section titled “Values”A bare word works when it has no spaces, no operators and does not start with a digit:
scope == dev.kingtuxrelease_type == SnapshotQuote anything else:
name == "my package"version ~= "1.*"description ~= "*deprecated*"Quoting is also how you write a value that starts with a digit without it being read as a number.
Dates are written YYYY-MM-DD and compared as timestamps:
created > 2024-01-01updated >= 2025-06-01 and updated < 2025-07-01Combining
Section titled “Combining”and bothor eithernot negate what follows( ) groupnot binds tighter than and, which binds tighter than or. Parenthesise when in doubt:
(scope == dev.kingtux or scope == com.example) and created > 2024-01-01Without the parentheses that reads as scope == dev.kingtux or (scope == com.example and created > 2024-01-01), which is a different question.
Examples
Section titled “Examples”# Everything in one repositoryrepo == releases
# One group, stable versions onlyscope == dev.kingtux and not release_type == Snapshot
# Published this yearcreated >= 2025-01-01
# API artifacts across two groupsname ~= *-api and (scope == dev.kingtux or scope == com.example)
# A specific package's prereleasesproject == @nitro/example and version ~= "*-beta.*"
# Everything but one storagenot storage == archiveErrors
Section titled “Errors”A parse failure comes back as a 400 with the message and the character range it happened at, so a
UI can underline the offending token rather than reporting “syntax error” for the whole string.
{ "error": "Unknown field `nmae`", "start": 0, "end": 4}Safety
Section titled “Safety”A compiled query is a SQL predicate containing only placeholders, plus the values to bind, so text from a query never becomes part of a statement. Column names come from the field enum rather than from the query, so there is nothing to escape there either.