Skip to content

Query Configs and quote_mode Guide

This guide explains how to configure per-query behavior with Query Configs (qcfg) and how to test quote_mode correctly.

What To Know First

  • Exact match is done on normalized query text.
  • On create, send query only. The system computes normalized_query automatically.
  • quote_mode and query-level search_config are configured via Admin APIs (or index settings), not passed directly in the /search request body.
  • Do not edit Query Config records directly in DynamoDB. Use APIs so shard/artifact rebuild and KV sync are triggered.

How Exact Match Works

When a search request comes in, the Ecom API normalizes the incoming q and looks for a Query Config with the same normalized value.

Examples that normalize to the same value:

  • OrlĂ©ans
  • orleans
  • ORLEANS

If those normalize to the same key, they will all match the same qcfg.

quote_mode Values

For query air jordan 1:

quote_mode Output query form
no_quotes air jordan 1
phrase "air jordan 1"
token "air" "jordan" "1"
tokens_except_first air "jordan" "1"
tokens_except_last "air" "jordan" 1

Notes:

  • Per-query quote_mode in qcfg overrides index-level quote_mode.
  • no_quotes explicitly disables quoting (useful to override index-level quoting).
  • quote_mode is not applied for image queries, multi-term object queries, or searchMethod = TENSOR.
  • For lexical false positives like air jordan 1 matching air jordan 11, start with token.

Environment Setup

export ECOM_HOST=https://staging-ecom.dev-marqo.org
export HOST=https://staging-admin.dev-marqo.org

export SYSTEM_ACCOUNT_ID=<system_account_id>
export INDEX_NAME=<index_name>

Query Config Admin API Workflow

1) Check index details first

cloudflared access curl "$HOST/api/v1/admin/accounts/$SYSTEM_ACCOUNT_ID/indexes/$INDEX_NAME" | jq

Recommended check: confirm query config overrides are enabled for the index (feature_flags.qcfg_overrides).

2) Create query configs

cloudflared access curl "$HOST/api/v1/accounts/$SYSTEM_ACCOUNT_ID/indexes/$INDEX_NAME/query-configs" \
  -sS -X POST \
  -H "Content-Type: application/json" \
  --data '[
    {
      "query": "air jordan 1",
      "quote_mode": "token",
      "search_config": {
        "searchMethod": "HYBRID",
        "hybridParameters": { "alpha": 0.15 }
      },
      "updated_by": "you@marqo.ai",
      "reason_for_update": "Reduce lexical false positives for AJ1"
    }
  ]' | jq

Response shape is batch-based:

  • succeeded: items written
  • failed: item-level errors
  • HTTP 207 if partial success, 200 if all succeeded

3) Get one config

Use the normalized query in the path:

ENC=$(printf '%s' "air jordan 1" | jq -sRr @uri)
cloudflared access curl "$HOST/api/v1/accounts/$SYSTEM_ACCOUNT_ID/indexes/$INDEX_NAME/query-configs/$ENC" | jq

4) List configs

cloudflared access curl "$HOST/api/v1/accounts/$SYSTEM_ACCOUNT_ID/indexes/$INDEX_NAME/query-configs?limit=50" | jq
cloudflared access curl "$HOST/api/v1/accounts/$SYSTEM_ACCOUNT_ID/indexes/$INDEX_NAME/query-configs?limit=50&q_prefix=air%20jordan" | jq

5) Update configs

Use normalized_query for updates:

cloudflared access curl "$HOST/api/v1/accounts/$SYSTEM_ACCOUNT_ID/indexes/$INDEX_NAME/query-configs" \
  -sS -X PUT \
  -H "Content-Type: application/json" \
  --data '[
    {
      "normalized_query": "air jordan 1",
      "quote_mode": "phrase",
      "updated_by": "you@marqo.ai",
      "reason_for_update": "Try phrase mode"
    }
  ]' | jq

Nullable update behavior:

  • Omit quote_mode to keep the current value.
  • Send "quote_mode": null to clear it (inherit index-level setting).

6) Delete configs

Delete by normalized query keys:

cloudflared access curl "$HOST/api/v1/accounts/$SYSTEM_ACCOUNT_ID/indexes/$INDEX_NAME/query-configs" \
  -sS -X DELETE \
  -H "Content-Type: application/json" \
  --data-binary '["air jordan 1"]' | jq

How To Test quote_mode Correctly

Correct test flow

  1. Set quote_mode in qcfg (or index settings), not in /search body.
  2. Send Ecom search request with x-marqo-search-request: true.
  3. Inspect the response header x-marqo-search-request to verify what was sent to classic Marqo.

Example:

curl -sS -i \
  -H "x-marqo-index-id: ${SYSTEM_ACCOUNT_ID}-${INDEX_NAME}" \
  -H "x-marqo-search-request: true" \
  -H "Content-Type: application/json" \
  -X POST \
  --data '{"q":"air jordan 1","limit":20}' \
  "$ECOM_HOST/api/v1/indexes/$INDEX_NAME/search"

Look for x-marqo-search-request in the response headers:

  • If only quoting is active, expect quoted q.
  • If quoting + query_lexical_expansion are active, expect:
  • hybridParameters.queryTensor: unquoted query
  • hybridParameters.queryLexical: quoted query plus unquoted expansions
  • q removed

Representative header examples:

x-marqo-search-request: {"q":"\"air\" \"jordan\" \"1\"","limit":20,"searchMethod":"HYBRID","hybridParameters":{"alpha":0.15}}
x-marqo-search-request: {"limit":20,"searchMethod":"HYBRID","hybridParameters":{"alpha":0.15,"queryTensor":"air jordan 1","queryLexical":"\"air\" \"jordan\" \"1\" retro sneaker"}}

Notes:

  • Header value is JSON serialized as a single-line string.
  • Exact extra fields vary by index search_config; focus on q vs queryTensor/queryLexical.

Common mistake

Do not send quote_mode in this /search payload:

{
  "q": "mens basketball shoes",
  "quote_mode": "token"
}

That does not configure qcfg behavior. Configure quote_mode through Query Config APIs or index settings.

Manual Classic API Comparison (Optional)

If you want to manually simulate quoting behavior against classic Marqo:

  • Quote queryLexical manually.
  • Keep queryTensor unquoted.
  • Do not send q when sending queryTensor or queryLexical.

Use this only as a behavior check. To test real qcfg-driven quote_mode, go through Ecom API.

Troubleshooting Checklist

  • No difference between modes:
  • Confirm quote_mode is stored in qcfg (GET query-config endpoint).
  • Confirm you are checking x-marqo-search-request response header.
  • Confirm search method is not tensor-only.
  • Config not matching:
  • Confirm the incoming query normalizes to your qcfg key.
  • Remember exact matching is normalized-string equality.
  • Unexpected behavior after direct table edits:
  • Revert to Admin API writes only; direct DDB writes bypass rebuild/sync workflows.