Fill in the form below and watch the file build itself on the right. Nothing here leaves your browser — there is no server, no upload, and nothing is saved until you press Download. Every setting shows its plain-English purpose plus the exact key it writes, so this page stays useful for developers, managers and testers alike.
QF_API_KEY) — the key itself stays wherever you run the service. Pasting an actual key is rejected, same as the loader does.What is a single record here called? This name has to match exactly what your code asks for later, so pick one word you'll be comfortable using everywhere — like Order or Customer.
Pick which AI service turns a typed question into a real query. Skip this whole step if you'll only ever send ready-made queries — that path never calls a model at all.
Set creativity to 0 so the same question always compiles the same way. Only turn on "Force JSON replies" for an endpoint you've tested — some providers return broken JSON with it on.
models[]Optional. If the main model is unreachable or rate-limited, QueryForge tries these in order and uses whichever answers first.
Point your data's name at the real table or collection in each database you use. Skip this and the name from Step 1 is used as-is. Note that sql specifically means PostgreSQL — MySQL is its own option because the two speak slightly different SQL.
Elasticsearch/OpenSearch can't be combined with other databases — remove the backends above to enable it.
This is the whole vocabulary. Anything listed here can be asked about; anything left out cannot be invented — the model is told to say so rather than guess. At least one field is required.
Shown to the AI so it understands a field name like this one. Required — the loader rejects a custom field with no description.
The AI can't see real values in a free-text field the way it can an enum's list — this stands in for that. Required for a custom free-text field.
This is an enum field, so its required context is the Allowed values list below, not a value hint.
Leave empty unless this field is both full-text searched (contains) and exactly matched, sorted, or aggregated — a single ES field can't do both.
Anything outside this list is refused before a query is even built.
Use this when the column always stores one case but people type another.
Put in what people actually type — this is what makes ordinary questions resolve to this field.
operatorsMarked with a dot: no value type on a {{ item.type }} field can ever satisfy that comparison — it's listed so you can see what's ruled out.
elemMatchPick the array only when it truly is a list. Declaring it makes two conditions apply to one element; leaving it off lets them match two different elements.
nestedPathOnly for an ES mapping type of "nested" (not "object"). Declaring it makes two conditions apply to one element; leaving it off lets them match two different elements.
Indexed and priority never reject a query — they only decide what's checked first.
A tenancy / scope field (subscriptionId, userId…) is one your app forces onto every query. It's added as not-askable so the model never sees it — you pass its value yourself at call time.
{{ schemaImportHint }}
| name | type | notes | |
|---|---|---|---|
| {{ row.name }} |
{{ row.notes }} |
Nothing here leaves your browser — the pasted text is parsed on this page only, exactly like the JSON import below. Every imported row still opens as an ordinary field card afterward, so review anything flagged before downloading.
Which index/indexes {{ esProduct }} searches. QueryForge only ever returns the generated query DSL and the resolved index/path — nothing here executes a request.
No field is marked "Use for Elasticsearch index routing" yet — add that in a field's Advanced > Hints & bounds (Step 4) first.
The token inside {} is decorative — any name — and is replaced with the routing field's literal value.
No date field is marked "Use for Elasticsearch index routing" yet — add that in a field's Advanced > Hints & bounds (Step 4) first.
Filled in automatically when you change granularity — the token must exactly match it. A date range expands to every partition it spans.
No field is marked "Use for Elasticsearch index routing" yet — add that in a field's Advanced > Hints & bounds (Step 4) first.
Every matching rule is a candidate; the highest priority wins. Two matches tied at the same priority is rejected as ambiguous rather than guessed.
No field is marked "Use for Elasticsearch index routing" yet — add that in a field's Advanced > Hints & bounds (Step 4) first.
Evaluated in order — the first matching branch wins.
Used when the routing field is absent from the query, unresolvable, or nothing matches. Required for every strategy — QueryForge never searches an unrestricted wildcard.
The result window and a few deterministic safety limits. All optional, all cheap insurance.
Page size applies when a question names none; the ceiling can't be exceeded even if asked; nesting bounds how deep "and / or" groups can go — 0 means unlimited.
policy.requiredScopeYour app passes the signed-in user's tenant or account id with every query, and QueryForge locks the query to it. This makes passing it compulsory — forget it once and the query is refused instead of quietly returning everyone's rows.
QueryForge will refuse any query missing {{ rc.name }}
Add at least one name, or nothing is enforced.
A blank value is refused too — an empty tenant id matches no rows, which looks exactly like an account with no data.
policy.denyRegexOnWorth checking on any free-text field a user can steer — an unchecked pattern can be a denial-of-service risk.
Add a field first.
policy.requiresRequire one field whenever another is used — e.g. passport expiry always needs a country. Leave this empty if every field here already stands on its own.
Add a field first.
Load a file to edit it, or start from a working example. This replaces everything currently in the form.