🏹Dynamic Parameters/Query Time weights

How you can use Qyver to achieve quality retrieval, through implementing query time weights - at both query definition and query execution.

Achieving High-Quality Retrieval with Qyver: Query Time Weighting

Getting high-quality results from a vector database query is challenging. Through real-world machine learning deployment, we’ve learned two fundamental truths:

1. The richer your dataset, the better your retrieval results—but only if your embeddings fully represent that dataset. 2. Different use cases emphasize different data attributes—your retrieval system must prioritize what matters most.

How Qyver Handles This Challenge

Instead of embedding all data as a single text string, Qyver’s Spaces allow you to:

  • Embed attributes separately rather than forcing everything into a single vector.

  • Concatenate attribute-specific embeddings into a multimodal vector.

  • Prioritize different data attributes dynamically at query time, avoiding the need for reranking.

The result? Faster, higher-quality retrieval—without expensive post-processing layers or reranking.


Two Ways to Weight a Query

Qyver enables query weighting in two ways:

1. Weighting at Query Definition

  • Set weights when defining a query—experiment and optimize without re-embedding your dataset.

2. Weighting at Query Execution

  • Set weights dynamically when running the query—fine-tune retrieval in real-time, without re-indexing.

Let’s see how both approaches work in Qyver.


1. Query Weighting at Definition

By structuring separate embeddings for different attributes, Qyver Spaces allow you to weight each attribute independently.

Example: Optimizing Search for Text vs. Popularity

Let’s define a schema and two Spaces:

@schema
class Paragraph:
    id: IdField
    body: String
    like_count: Integer

paragraph = Paragraph()

# Define separate embeddings for text similarity and numerical ranking
body_space = TextSimilaritySpace(
    text=paragraph.body, model="sentence-transformers/all-mpnet-base-v2"
)
like_space = NumberSpace(
    number=paragraph.like_count, min_value=0, max_value=100, mode=Mode.MAXIMUM
)

# Combine Spaces into an index
paragraph_index = Index([body_space, like_space])

Adding Data to the System

source: InMemorySource = InMemorySource(paragraph)
executor = InMemoryExecutor(sources=[source], indices=[paragraph_index])
app = executor.run()

source.put([
    {"id": "paragraph-1", "body": "Glorious animals live in the wilderness.", "like_count": 75},
    {"id": "paragraph-2", "body": "Growing computation power enables advancements in AI.", "like_count": 10},
])

Now, let's define two different queries:

  • One that prioritizes text similarity (weights text 2x more than likes).

  • One that prioritizes likes (weights likes 2x more than text).

body_query = (
    Query(
        paragraph_index,
        weights={
            body_space: 1.0,  # Text similarity weighted higher
            like_space: 0.5,
        },
    )
    .find(paragraph)
    .similar(body_space.text, "What makes the AI industry go forward?")
)

like_query = (
    Query(
        paragraph_index,
        weights={
            body_space: 0.5,
            like_space: 1.0,  # Like count weighted higher
        },
    )
    .find(paragraph)
    .similar(body_space.text, "What makes the AI industry go forward?")
)

Running the Queries

1. Prioritizing Text Similarity

body_result = app.query(body_query)
body_result.to_pandas()

Expected Output:

body
like_count
id

Growing computation power enables advancements in AI

10

paragraph-2

Glorious animals live in the wilderness

75

paragraph-1

2. Prioritizing Like Count

like_result = app.query(like_query)
like_result.to_pandas()

Expected Output:

body
like_count
id

Glorious animals live in the wilderness

75

paragraph-1

Growing computation power enables advancements in AI

10

paragraph-2

Why is this better?

  • No re-embedding needed—just adjust query weights.

  • No reranking needed—Qyver retrieves relevant results upfront.


2. Dynamic Query Weighting at Execution

In production systems, query logic is typically predefined. But Qyver allows dynamic fine-tuning, so data scientists or users can adjust weighting at runtime—without modifying query definitions.

Defining a Query with Dynamic Parameters

query = (
    Query(
        paragraph_index,
        weights={
            body_space: Param("body_space_weight"),
            like_space: Param("like_space_weight"),
        },
    )
    .find(paragraph)
    .similar(body_space.text, Param("query_text"))
)

Running the Query with Different Weights

1. Prioritizing Text Similarity

body_based_result = app.query(
    query,
    query_text="How computation power changed the course of AI?",
    body_space_weight=1,
    like_space_weight=0,
)

body_based_result.to_pandas()

Expected Output:

body
like_count
id

Growing computation power enables advancements in AI

10

paragraph-2

Glorious animals live in the wilderness

75

paragraph-1

2. Prioritizing Like Count

like_based_result = app.query(
    query,
    query_text="How computation power changed the course of AI?",
    body_space_weight=0,
    like_space_weight=1,
)

like_based_result.to_pandas()

Expected Output:

body
like_count
id

Glorious animals live in the wilderness

75

paragraph-1

Growing computation power enables advancements in AI

10

paragraph-2

Why is this powerful?

  • Fine-tune relevance dynamically without touching embeddings.

  • Empower users to control results without developer intervention.


Final Thoughts

Qyver’s Query Time Weighting:

  1. Weighting at Query Definition → Experiment & optimize without re-embedding data.

  2. Weighting at Query Execution → Allow real-time fine-tuning for data scientists and users.

What does this mean for you?

  • Faster, more relevant search results—without reranking overhead.

  • Greater flexibility & adaptability—retrieval adjusts dynamically.

  • No costly infrastructure changes—just tweak the query weights!

Like what we’re doing? Give us a star!

Last updated