Query profiles let you control ranking at query time without modifying your schema. Each profile bundles a ranking profile, function weights, and query parameters into a named configuration that you can switch between requests.

For the concepts behind query profiles, see Anatomy of a Vespa application.

Default Query Profile

Default Query Profile

When you create a schema with create_default_schema() or create_schema(), the plugin generates a default query profile automatically. It includes:

  • A YQL query for hybrid search (keyword + vector) or keyword-only search
  • The weighted-rank2 ranking profile
  • Query type fields for all function weights

The default profile name matches the schema name, unless you override it with default_query_profile_name:

create_default_schema(
    name="articles",
    mode=SearchMode.INDEX,
    embedding_dimensions=1024,
    schema_version=1,
    default_query_profile_name="articles-default",
)

With the default profile, all ranking weights start at 0. Set one or more to a non-zero value when querying to activate ranking.

Creating Custom Query Profiles

Creating Custom Query Profiles

First, generate a new migration file:

uv run mistral-vespa generate-migration --app-dir ./vespa_app add_custom_query_profiles

Then edit the generated file to define your profiles using add_query_profiles:

from vespa.package import QueryField

from mistralai.search.toolkit.plugins.vespa.app.schemas.query_profile import QueryProfile
from mistralai.search.toolkit.plugins.vespa.migration import VespaMigration, add_query_profiles


class AddCustomQueryProfiles(VespaMigration):
    def migrate(self) -> None:
        add_query_profiles(
            [
                QueryProfile(
                    name="keyword-search",
                    fields=[
                        QueryField(name="ranking.features.query(bm25_title_weight)", value=100),
                        QueryField(name="ranking.features.query(bm25_content_weight)", value=50),
                        QueryField(name="hits", value=10),
                        QueryField(name="ranking.profile", value="weighted-rank1"),
                    ],
                ),
                QueryProfile(
                    name="hybrid-search",
                    fields=[
                        QueryField(name="ranking.features.query(bm25_title_weight)", value=100),
                        QueryField(
                            name="ranking.features.query(content_embedding_cosine_similarity_score_weight)",
                            value=80,
                        ),
                        QueryField(
                            name="ranking.features.query(freshness_created_at_weight)",
                            value=20,
                        ),
                        QueryField(name="hits", value=10),
                        QueryField(name="ranking.profile", value="weighted-rank2"),
                    ],
                ),
            ]
        )

Each QueryProfile has:

ParameterTypeDescription
namestrProfile name, used to select it at query time
fieldslist[QueryField]List of query fields with preset values

Each QueryField has:

ParameterTypeDescription
namestrVespa query parameter name
valuestr | int | floatDefault value for the parameter
Common Query Fields

Common Query Fields

FieldDescription
ranking.profileRanking profile to use (weighted-rank1, weighted-rank2, match-only)
ranking.features.query(<weight_name>)Weight for a ranking function (see Ranking reference)
hitsNumber of results to return

Weight names follow the pattern <function_name>_weight. For example, bm25_title has weight bm25_title_weight.

Versioned Query Profiles

Versioned Query Profiles

Use a colon-separated version string to maintain multiple versions of a profile:

add_query_profiles(
    [
        QueryProfile(
            name="search:1.0.0",
            fields=[
                QueryField(name="ranking.features.query(bm25_title_weight)", value=50),
                QueryField(name="hits", value=3),
                QueryField(name="ranking.profile", value="weighted-rank2"),
            ],
        ),
        QueryProfile(
            name="search:2.0.0",
            fields=[
                QueryField(name="ranking.features.query(bm25_title_weight)", value=100),
                QueryField(
                    name="ranking.features.query(freshness_created_at_weight)",
                    value=20,
                ),
                QueryField(name="hits", value=5),
                QueryField(name="ranking.profile", value="weighted-rank2"),
            ],
        ),
    ]
)

Select the version when creating a QueryProfile at query time:

from mistralai.search.toolkit.plugins.vespa.search import QueryProfile

query_profile = QueryProfile(name="search", version="2.0.0")
Using Query Profiles at Query Time

Using Query Profiles at Query Time

Pass a QueryProfile to app.get_search_index() to select which profile to use for retrieval:

import os

from mistralai.search.toolkit.plugins.vespa import VespaClientConfig
from mistralai.search.toolkit.plugins.vespa.search import QueryProfile
from vespa_app import app

collection_name = "articles"
vespa_config = VespaClientConfig(
    endpoint=os.environ.get("VESPA_ENDPOINT", "http://localhost:18080"),
)
vector_store = app.get_search_index(
    vespa_config,
    collection_name=collection_name,
    query_profile=QueryProfile(name="hybrid-search"),
)

When query_profile is omitted, the schema's default query profile is used automatically.

See Also

See Also