# Key Vespa Architecture Questions: Ranking, Deployment & Queries

**URL:** <https://discuss.huggingface.co/t/key-vespa-architecture-questions-ranking-deployment-queries/172076>\
**Category:** Community Calls\
**Created:** [December 29, 2025, 5:53pm UTC](https://discuss.huggingface.co/t/key-vespa-architecture-questions-ranking-deployment-queries/172076 "2025-12-29T17:53:42Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![im-atulya](https://avatars.discourse-cdn.com/v4/letter/i/ba9def/32.png) [@im-atulya](https://discuss.huggingface.co/u/im-atulya)\
**Post date:** [December 29, 2025, 5:53pm UTC](https://discuss.huggingface.co/t/key-vespa-architecture-questions-ranking-deployment-queries/172076/1 "2025-12-29T17:53:42Z")

</div>

Hello all, we’re implementing Vespa for a production search system migrating from Solr, and have several architectural questions that we couldn’t find clear answers for in the documentation. We’re particularly interested in understanding Vespa’s flexibility around ranking and query execution compared to Solr’s dynamic capabilities.

* * *

**Questions:**

**1. External Rank Profiles**  
Is there support for external rank profiles or ranking configuration files? We need to modify ranking logic frequently without full application redeployment. Can rank profiles be defined in separate files that are loaded dynamically, or updated via API?

**2. Query-Time Ranking Definition**  
Can ranking expressions be passed in queries instead of pre-defined in schemas? We want to define ranking logic at query execution time for A/B testing without schema changes. Is there support for inline ranking expressions or parameterized rank profiles?

**3. Deployment Impact on Search**  
How does Vespa handle search requests during application deployment? Is there any query disruption, partial availability, or performance degradation? What’s the recommended approach for zero-downtime ranking updates?

**4. Relevance Score with Filters Only**  
When using only attribute filters (e.g., `where category = 'X' and price > 100`), why do results have relevance scores \> 0? What calculation produces this score when no text matching is involved?

**5. Sort by Relevance Ascending**  
How can we sort search results by lowest relevance first? The default sorts highest-first. `order by relevance asc` fails with “no sortable attribute.” Are there workarounds to get least relevant results?

**6. Grouping Order Logic**  
For grouping queries with `| all(group(field))`, in what order are groups returned? Is it by frequency, field value, or document order in original query? Can this order be controlled?

**7. Dynamic Field Selection for userQuery()**  
Can we specify which fields `userQuery()` searches at query time? Currently it uses schema/query-profile defaults. We need Solr-like `qf` parameter functionality to dynamically change searched fields per query.

* * *

**For those who have made similar migrations from Solr/Elasticsearch to Vespa** , we’d especially like to know about your experiences with these specific challenges. Any references to documentation, benchmarks, or real-world experiences would be greatly appreciated!

---

<div class="post-metadata">

**Author:** ![John6666](https://sea2.discourse-cdn.com/hellohellohello/user_avatar/discuss.huggingface.co/john6666/32/27664_2.png) [@John6666](https://discuss.huggingface.co/u/John6666)\
**Post date:** [December 29, 2025, 9:57pm UTC](https://discuss.huggingface.co/t/key-vespa-architecture-questions-ranking-deployment-queries/172076/2 "2025-12-29T21:57:25Z")

</div>

For now:

* * *

## Context: why these questions feel “harder” in Vespa than in Solr

Solr encourages request-time dynamism: field lists (`qf`), boosts, and sometimes scoring tweaks can be pushed into query params. Vespa is designed around a different contract:

- **Ranking logic lives in the application package** as _rank profiles_ and is compiled and validated as part of deployment. ([VespaDocument](https://docs.vespa.ai/en/ranking/ranking-expressions-features.html "Ranking Expressions and Features"))
- **Request-time flexibility is mostly “select a pre-defined profile” + “pass inputs into that profile.”** ([VespaDocument](https://docs.vespa.ai/en/querying/query-api.html "Query API"))
- This yields predictable latency and safety (invalid changes rejected before activation), but you must engineer experimentation differently than Solr. ([VespaDocument](https://docs.vespa.ai/en/basics/applications.html "Vespa applications"))

With that framing, here are direct answers and the production patterns that map closest to your Solr expectations.

* * *

## 1) External rank profiles. Separate files? Dynamically loaded? Updated by API?

### What Vespa supports

**Yes, external ranking config files exist, but they are not “dynamic runtime plugins.”**

- You can define rank profiles either inline in the schema or in separate files:  
`schemas/<schema-name>/<profile-name>.profile`. ([VespaDocument](https://docs.vespa.ai/en/basics/ranking.html "Ranking"))
- Rank profiles can also inherit from each other to reduce duplication. ([VespaDocument](https://docs.vespa.ai/en/basics/ranking.html "Ranking"))

This solves “giant schema file” and supports clean modularity.

### What Vespa does not support

**No, you cannot hot-load or update rank profiles via query-time API calls.**  
Rank expressions are stored in a rank profile, and rank profiles are part of the deployed config. ([VespaDocument](https://docs.vespa.ai/en/ranking/ranking-expressions-features.html "Ranking Expressions and Features"))

### The production workaround that gives Solr-like iteration speed

Engineer rank profiles as **templates** plus **query-time inputs** :

- Deploy stable rank profiles infrequently.
- Change weights and thresholds frequently via `input.query(...)` or other query features. The Query API explicitly supports passing input data used in ranking. ([VespaDocument](https://docs.vespa.ai/en/querying/query-api.html "Query API"))
- For “structural” changes (new features, new phases, new expression graph), deploy. Deployments are designed to be safe under live traffic. ([VespaDocument](https://docs.vespa.ai/en/basics/applications.html "Vespa applications"))

* * *

## 2) Query-time ranking definition. Can we send ranking expressions in the query?

### Direct answer

**No. You cannot pass an arbitrary ranking expression in a request and have Vespa execute it.**  
This is a known limitation and is commonly explained as an efficiency/compilation issue. ([Stack Overflow](https://stackoverflow.com/questions/49314786/passing-ranking-function-in-query-text "Passing Ranking function in query text - vespa"))

Related: even some deeper rank-feature configuration knobs (example: bm25 properties) are explicitly “rank-profile only,” not request-settable. ([GitHub](https://github.com/vespa-engine/vespa/issues/31211 "Allow specifying rank feature configuration using query ..."))

### What you do instead for A/B testing

You get two practical degrees of freedom that cover most experimentation needs:

1. **Choose among pre-defined rank profiles at query time**  
`ranking.profile=<name>` (often abbreviated as `ranking=<name>`). Vespa docs describe this selection model. ([VespaDocument](https://docs.vespa.ai/en/basics/ranking.html "Ranking"))

2. **Parameterize rank profiles**  
Define expressions that read query features and set them per request:

- `input.query(w_title)=2.0`
- `input.query(w_body)=0.5`
- etc. The Query API guide shows query inputs as a first-class part of requests. ([VespaDocument](https://docs.vespa.ai/en/querying/query-api.html "Query API"))

**High-leverage pattern:** pre-deploy a small family of profiles (`baseline`, `expA`, `expB`) and then vary weights via query inputs. This matches how Vespa teams describe running experiments without redeploying. ([Medium](https://medium.com/vespa/billion-scale-vector-search-with-vespa-part-two-bac817b30286 "Billion-scale vector search with Vespa — part two"))

* * *

## 3) Deployment impact on search. Any disruption? How to do zero-downtime ranking updates?

### What Vespa promises (and generally delivers)

Vespa documentation is explicit:

- Deploying application package changes is generally safe at any time.
- **It does not disrupt queries and writes.**
- Invalid or destructive changes are rejected before taking effect. ([VespaDocument](https://docs.vespa.ai/en/basics/applications.html "Vespa applications"))

Vespa Cloud adds orchestrated multi-zone deployments and CD hooks. ([VespaDocument](https://docs.vespa.ai/en/operations/automated-deployments.html "Automated Deployments"))

### The real operational nuance

Even when requests are not dropped, **some changes can increase load** :

- Schema/indexing changes that trigger reindexing.
- Changes that increase ranking cost per hit or increase `rerank-count` in phased ranking. ([VespaDocument](https://docs.vespa.ai/en/ranking/phased-ranking.html "Phased Ranking"))

So, “no disruption” is about _availability_. “No performance change” depends on what you deployed.

### Recommended approach for zero-downtime ranking iteration

Use a two-lane strategy:

- **Lane A: No-deploy iteration**  
Keep rank profile structure stable. Change weights via query inputs. ([VespaDocument](https://docs.vespa.ai/en/querying/query-api.html "Query API"))

- **Lane B: Safe deploy iteration**  
When structure must change, deploy new rank profile names (or inherited variants), then switch traffic gradually by selecting `ranking.profile` per request. ([VespaDocument](https://docs.vespa.ai/en/basics/ranking.html "Ranking"))

This maps closely to how teams run experiments: predefine multiple profiles and select them in the query. ([Medium](https://medium.com/vespa/billion-scale-vector-search-with-vespa-part-two-bac817b30286 "Billion-scale vector search with Vespa — part two"))

* * *

## 4) Relevance score with filters only. Why is relevance \> 0 with no text match?

### Background: “relevance” is just the output of the active rank profile

In Vespa, each hit’s `relevance` is the rank score computed by the chosen rank profile. ([VespaDocument](https://docs.vespa.ai/en/ranking/ranking-expressions-features.html "Ranking Expressions and Features"))

If you do not specify a profile, Vespa uses:

- `default` if present in your schema
- otherwise a builtin default ranking by `nativeRank`. ([VespaDocument](https://docs.vespa.ai/en/basics/ranking.html "Ranking"))

### Why filter-only queries still show non-zero

Because rank expressions can be **query-independent** or mostly query-independent:

- A rank profile can use **document features** only (attributes), producing a non-zero score even without text matching. Vespa’s ranking intro shows an example where the relevance equals `attribute(inlinks).count`, explicitly noting that the expression does not use query features. ([VespaDocument](https://docs.vespa.ai/en/ranking/ranking-intro.html "Introduction to ranking"))

Also note a subtle pitfall:

- Even if you override ordering with `order by`, Vespa still computes the rank score unless you choose `unranked`. The YQL reference states Vespa does not optimize away rank-score computation just because you discard it via sorting. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))

### If you truly want “no ranking” for filter endpoints

Use the built-in rank profile `unranked`:

- `ranking.profile=unranked`  
This is explicitly recommended for optimal performance of sorting queries. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))

### How to debug “where did this score come from?”

Turn on rank feature listing for debugging and offline analysis:

- `ranking.listFeatures=true` is documented in the ML tutorial as a way to return rank features. ([VespaDocument](https://docs.vespa.ai/en/learn/tutorials/text-search-ml.html "Improving Text Search through ML"))

* * *

## 5) Sort by relevance ascending. How to get “least relevant first”?

### Direct answer

Use the special sorting attribute **`'[relevance]'`** and sort ascending:

- `order by '[relevance]' asc` ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))
- Sorting reference notes `[relevance]` defaults to descending if you omit direction, so you must be explicit. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/sorting-language.html "Sorting Reference"))

Example YQL:

```plaintext
select * from mydoc where userQuery() order by '[relevance]' asc;

```

If you want stable results when many documents have equal relevance, add a deterministic secondary sort (for example on an id attribute). The YQL reference recommends secondary ordering after `'[relevance]'`. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))

* * *

## 6) Grouping order logic. What order do groups come back in? Can we control it?

### Default group order

If you do not specify `order(...)` in the grouping expression, default ordering is:

- **`-max(relevance())`** (descending by the best hit’s relevance per group). ([VespaDocument](https://docs.vespa.ai/en/reference/querying/grouping-language.html "Grouping Reference"))

If your query is effectively “all documents” or relevance ties across groups, order will look random. The grouping guide explicitly calls this out. ([VespaDocument](https://docs.vespa.ai/en/querying/grouping.html "Grouping and aggregation"))

### How to control group order

Add `order(...)` at the group level. The grouping guide and reference both emphasize:

- `order(...)` controls **group ordering** , not hit ordering inside each group. ([VespaDocument](https://docs.vespa.ai/en/querying/grouping.html "Grouping and aggregation"))
- Use aggregators like `count()`, `avg()`, `sum()` for deterministic ordering after merges. The reference notes limitations for `max/min` ordering after merge operations. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/grouping-language.html "Grouping Reference"))

Practical examples:

- Most frequent groups first: `order(-count())`
- Least frequent first: `order(count())`
- Order by a numeric attribute aggregate: `order(-max(attribute(price)))`

### Important operational constraint

Fields used in grouping must be attributes. The Query API docs state this explicitly. ([VespaDocument](https://docs.vespa.ai/en/querying/query-api.html "Query API"))

* * *

## 7) Dynamic field selection for `userQuery()`. Solr `qf` equivalent?

### What Vespa does by default

If you do not specify fields in the query, Vespa searches a **fieldset named `default`** (or a field named `default`), if you defined one. The text search tutorial describes this and shows a `fieldset default { fields: ... }`. ([VespaDocument](https://docs.vespa.ai/en/learn/tutorials/text-search.html "Text Search Tutorial"))

### How to choose fields at query time

You typically do this by switching the default searched field or fieldset:

- Use `model.defaultIndex` to specify a field or fieldset if not using the default one. The YQL reference explicitly points to `model.defaultIndex` for this purpose. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))

So the Vespa version of Solr `qf` is usually:

- Predefine a handful of fieldsets (`qf_short`, `qf_long`, `qf_title_only`, …)
- Choose among them per request via `model.defaultIndex=<fieldset>` ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))

### Where boosts belong in Vespa

In Solr, boosts often live in `qf`. In Vespa, boosts usually live in **ranking** :

- weight `bm25(title)` vs `bm25(body)` etc using query inputs.
- That keeps query parsing stable and puts tuning where Vespa expects it. ([VespaDocument](https://docs.vespa.ai/en/querying/query-api.html "Query API"))

### How to keep this safe at scale

Use query profiles to provide defaults and reduce client complexity, and query profile types to restrict what parameters are legal (typed, optionally “no extra params”). Vespa docs describe query profile types explicitly. ([VespaDocument](https://docs.vespa.ai/en/querying/query-profiles.html "Query Profiles"))

* * *

## Migration reality check: what teams report when moving from Solr or Elasticsearch

If your system looks like “production commerce search with lots of filters, facets, and frequent tuning,” two public migrations are especially relevant:

- **Vinted** describes a multi-stage migration from Elasticsearch to Vespa, including switching search traffic and later facet traffic. ([Vinted Engineering](https://vinted.engineering/2024/09/05/goodbye-elasticsearch-hello-vespa/ "Vinted Search Scaling Chapter 8: Goodbye Elasticsearch ..."))
- **OkCupid** compares Vespa and Elasticsearch for large-scale matching and highlights operational scaling and distribution as a major reason for choosing Vespa. ([OkCupid Tech Blog](https://tech.okcupid.com/vespa-vs-elasticsearch-for-matching-millions-of-people-6e3af18eb4dc "Vespa vs. Elasticsearch for matching millions of people"))
- The **Solr vs Vespa guide** is specifically written to map Solr concepts to Vespa behavior (ranking defaults, query parameters, request model). ([Vespa Blog](https://blog.vespa.ai/solr-vs-vespa/ "Vespa Guide for Solr Users"))

What tends to surprise Solr teams (and directly matches your questions):

1. **“Dynamic ranking” becomes “dynamic inputs + profile selection.”** You do not ship new expressions per request. ([Stack Overflow](https://stackoverflow.com/questions/49314786/passing-ranking-function-in-query-text "Passing Ranking function in query text - vespa"))
2. **Filter and group features are fast when modeled as attributes** , but you must decide that up front. ([VespaDocument](https://docs.vespa.ai/en/querying/query-api.html "Query API"))
3. **Sorting does not remove ranking cost** unless you switch to `unranked`. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))

* * *

## A concrete architecture that fits your “Solr-like agility” requirement

If your core requirement is “we tune ranking a lot, and we do A/B tests constantly,” the most robust Vespa pattern is:

1. **Define a small family of rank profiles** (structural variants). ([VespaDocument](https://docs.vespa.ai/en/basics/ranking.html "Ranking"))

2. **Parameterize everything you expect to tune weekly or daily** using query inputs (`input.query(...)`). ([VespaDocument](https://docs.vespa.ai/en/querying/query-api.html "Query API"))

3. **Do A/B routing outside Vespa** (gateway or search service) and set:

4. **Make filter-only endpoints use `unranked`** so you are not paying for rank-score computation you do not use. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))

5. **Always specify group ordering** in grouping expressions if you need deterministic behavior. ([VespaDocument](https://docs.vespa.ai/en/querying/grouping.html "Grouping and aggregation"))

6. **Use debug tooling sparingly but deliberately** (`ranking.listFeatures=true`) to explain scoring and build training or regression datasets. ([VespaDocument](https://docs.vespa.ai/en/learn/tutorials/text-search-ml.html "Improving Text Search through ML"))

* * *

## Summary bullets

- Rank profiles can live in separate `.profile` files, but they are deployed config, not hot-loaded runtime files. ([VespaDocument](https://docs.vespa.ai/en/basics/ranking.html "Ranking"))
- You cannot send ranking expressions in a query. Use profile selection plus query inputs for experiments. ([Stack Overflow](https://stackoverflow.com/questions/49314786/passing-ranking-function-in-query-text "Passing Ranking function in query text - vespa"))
- Deployments are designed to be online and not disrupt queries and writes. Performance impact depends on what changed. ([VespaDocument](https://docs.vespa.ai/en/basics/applications.html "Vespa applications"))
- Filter-only queries still get a rank score because ranking still runs unless you use `unranked`. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))
- Sort by lowest relevance using `order by '[relevance]' asc`. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))
- Grouping defaults to `-max(relevance())` and can look random on ties. Control it with `order(...)`. ([VespaDocument](https://docs.vespa.ai/en/querying/grouping.html "Grouping and aggregation"))
- Solr `qf` maps to schema fieldsets plus `model.defaultIndex` and ranking-time boosts. ([VespaDocument](https://docs.vespa.ai/en/reference/querying/yql.html "YQL Query Language Reference - Vespa Documentation"))
