Merge pull request #2357 from qdrant/hybrid-search-gap-3

Document fusion methods: weighted RRF, DBSF, FormulaQuery
This commit is contained in:
Dylan Couzon
2026-05-20 12:48:26 -04:00
committed by GitHub
43 changed files with 1120 additions and 32 deletions
@@ -0,0 +1 @@
This code snippet runs a hybrid query that fuses sparse and dense results with Distribution-Based Score Fusion (DBSF). DBSF normalizes each retriever's score distribution using the mean and three standard deviations as limits, then sums the normalized scores. Use it when the raw scores carry magnitude information you want to preserve, rather than discarding score information as RRF does.
@@ -0,0 +1,31 @@
using Qdrant.Client;
using Qdrant.Client.Grpc;
public class Snippet
{
public static async Task Run()
{
var client = new QdrantClient("localhost", 6334);
await client.QueryAsync(
collectionName: "{collection_name}",
prefetch: new List < PrefetchQuery > {
new() {
Query = new(float, uint)[] {
(0.22f, 1), (0.8f, 42),
},
Using = "sparse",
Limit = 20
},
new() {
Query = new float[] {
0.01f, 0.45f, 0.67f
},
Using = "dense",
Limit = 20
}
},
query: Fusion.Dbsf
);
}
}
@@ -0,0 +1,27 @@
```csharp
using Qdrant.Client;
using Qdrant.Client.Grpc;
var client = new QdrantClient("localhost", 6334);
await client.QueryAsync(
collectionName: "{collection_name}",
prefetch: new List < PrefetchQuery > {
new() {
Query = new(float, uint)[] {
(0.22f, 1), (0.8f, 42),
},
Using = "sparse",
Limit = 20
},
new() {
Query = new float[] {
0.01f, 0.45f, 0.67f
},
Using = "dense",
Limit = 20
}
},
query: Fusion.Dbsf
);
```
@@ -0,0 +1,29 @@
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Prefetch: []*qdrant.PrefetchQuery{
{
Query: qdrant.NewQuerySparse([]uint32{1, 42}, []float32{0.22, 0.8}),
Using: qdrant.PtrOf("sparse"),
Limit: qdrant.PtrOf(uint64(20)),
},
{
Query: qdrant.NewQueryDense([]float32{0.01, 0.45, 0.67}),
Using: qdrant.PtrOf("dense"),
Limit: qdrant.PtrOf(uint64(20)),
},
},
Query: qdrant.NewQueryFusion(qdrant.Fusion_DBSF),
})
```
@@ -0,0 +1,30 @@
```java
import static io.qdrant.client.QueryFactory.fusion;
import static io.qdrant.client.QueryFactory.nearest;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Fusion;
import io.qdrant.client.grpc.Points.PrefetchQuery;
import io.qdrant.client.grpc.Points.QueryPoints;
import java.util.List;
QdrantClient client = new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName("{collection_name}")
.addPrefetch(PrefetchQuery.newBuilder()
.setQuery(nearest(List.of(0.22f, 0.8f), List.of(1, 42)))
.setUsing("sparse")
.setLimit(20)
.build())
.addPrefetch(PrefetchQuery.newBuilder()
.setQuery(nearest(List.of(0.01f, 0.45f, 0.67f)))
.setUsing("dense")
.setLimit(20)
.build())
.setQuery(fusion(Fusion.DBSF))
.build())
.get();
```
@@ -0,0 +1,22 @@
```python
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.query_points(
collection_name="{collection_name}",
prefetch=[
models.Prefetch(
query=models.SparseVector(indices=[1, 42], values=[0.22, 0.8]),
using="sparse",
limit=20,
),
models.Prefetch(
query=[0.01, 0.45, 0.67], # <-- dense vector
using="dense",
limit=20,
),
],
query=models.FusionQuery(fusion=models.Fusion.DBSF),
)
```
@@ -0,0 +1,21 @@
```rust
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{Fusion, PrefetchQueryBuilder, Query, QueryPointsBuilder};
let client = Qdrant::from_url("http://localhost:6334").build()?;
client.query(
QueryPointsBuilder::new("{collection_name}")
.add_prefetch(PrefetchQueryBuilder::default()
.query(Query::new_nearest([(1, 0.22), (42, 0.8)].as_slice()))
.using("sparse")
.limit(20u64)
)
.add_prefetch(PrefetchQueryBuilder::default()
.query(Query::new_nearest(vec![0.01, 0.45, 0.67]))
.using("dense")
.limit(20u64)
)
.query(Query::new_fusion(Fusion::Dbsf))
).await?;
```
@@ -0,0 +1,26 @@
```typescript
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.query("{collection_name}", {
prefetch: [
{
query: {
values: [0.22, 0.8],
indices: [1, 42],
},
using: 'sparse',
limit: 20,
},
{
query: [0.01, 0.45, 0.67],
using: 'dense',
limit: 20,
},
],
query: {
fusion: 'dbsf',
},
});
```
@@ -0,0 +1,33 @@
package snippet
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
func Main() {
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
if err != nil { panic(err) } // @hide
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Prefetch: []*qdrant.PrefetchQuery{
{
Query: qdrant.NewQuerySparse([]uint32{1, 42}, []float32{0.22, 0.8}),
Using: qdrant.PtrOf("sparse"),
Limit: qdrant.PtrOf(uint64(20)),
},
{
Query: qdrant.NewQueryDense([]float32{0.01, 0.45, 0.67}),
Using: qdrant.PtrOf("dense"),
Limit: qdrant.PtrOf(uint64(20)),
},
},
Query: qdrant.NewQueryFusion(qdrant.Fusion_DBSF),
})
}
@@ -0,0 +1,22 @@
```http
POST /collections/{collection_name}/points/query
{
"prefetch": [
{
"query": {
"indices": [1, 42], // <┐
"values": [0.22, 0.8] // <┴─sparse vector
},
"using": "sparse",
"limit": 20
},
{
"query": [0.01, 0.45, 0.67, ...], // <-- dense vector
"using": "dense",
"limit": 20
}
],
"query": { "fusion": "dbsf" }, // <--- distribution-based score fusion
"limit": 10
}
```
@@ -0,0 +1,34 @@
package com.example.snippets_amalgamation;
import static io.qdrant.client.QueryFactory.fusion;
import static io.qdrant.client.QueryFactory.nearest;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Fusion;
import io.qdrant.client.grpc.Points.PrefetchQuery;
import io.qdrant.client.grpc.Points.QueryPoints;
import java.util.List;
public class Snippet {
public static void run() throws Exception {
QdrantClient client = new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName("{collection_name}")
.addPrefetch(PrefetchQuery.newBuilder()
.setQuery(nearest(List.of(0.22f, 0.8f), List.of(1, 42)))
.setUsing("sparse")
.setLimit(20)
.build())
.addPrefetch(PrefetchQuery.newBuilder()
.setQuery(nearest(List.of(0.01f, 0.45f, 0.67f)))
.setUsing("dense")
.setLimit(20)
.build())
.setQuery(fusion(Fusion.DBSF))
.build())
.get();
}
}
@@ -0,0 +1,20 @@
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.query_points(
collection_name="{collection_name}",
prefetch=[
models.Prefetch(
query=models.SparseVector(indices=[1, 42], values=[0.22, 0.8]),
using="sparse",
limit=20,
),
models.Prefetch(
query=[0.01, 0.45, 0.67], # <-- dense vector
using="dense",
limit=20,
),
],
query=models.FusionQuery(fusion=models.Fusion.DBSF),
)
@@ -0,0 +1,23 @@
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{Fusion, PrefetchQueryBuilder, Query, QueryPointsBuilder};
pub async fn main() -> anyhow::Result<()> {
let client = Qdrant::from_url("http://localhost:6334").build()?;
client.query(
QueryPointsBuilder::new("{collection_name}")
.add_prefetch(PrefetchQueryBuilder::default()
.query(Query::new_nearest([(1, 0.22), (42, 0.8)].as_slice()))
.using("sparse")
.limit(20u64)
)
.add_prefetch(PrefetchQueryBuilder::default()
.query(Query::new_nearest(vec![0.01, 0.45, 0.67]))
.using("dense")
.limit(20u64)
)
.query(Query::new_fusion(Fusion::Dbsf))
).await?;
Ok(())
}
@@ -0,0 +1,24 @@
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.query("{collection_name}", {
prefetch: [
{
query: {
values: [0.22, 0.8],
indices: [1, 42],
},
using: 'sparse',
limit: 20,
},
{
query: [0.01, 0.45, 0.67],
using: 'dense',
limit: 20,
},
],
query: {
fusion: 'dbsf',
},
});
@@ -0,0 +1,3 @@
This code snippet shows the canonical pattern for combining hybrid search fusion with business-logic ranking. The inner prefetch fuses sparse and dense results with RRF, then the outer `FormulaQuery` applies exponential decay on a `published_at` payload field to boost recent documents. The decay term is wrapped in `mult` with a `0.1` coefficient so it nudges the ranking rather than crowding out the small RRF scores. Use this pattern any time you want fusion plus recency, popularity, geo decay, or category-conditional multipliers, rather than trying to encode those signals as fusion weights.
For production: `published_at` must exist on every point, or supply `defaults` in the `FormulaQuery` to fill missing values. A datetime payload index on `published_at` keeps query latency low.
@@ -0,0 +1,60 @@
using Qdrant.Client;
using Qdrant.Client.Grpc;
public class Snippet
{
public static async Task Run()
{
var client = new QdrantClient("localhost", 6334);
await client.QueryAsync(
collectionName: "{collection_name}",
prefetch:
[
new PrefetchQuery {
Prefetch = {
new PrefetchQuery {
Query = new(float, uint)[] { (0.22f, 1), (0.8f, 42) },
Using = "sparse",
Limit = 100
},
new PrefetchQuery {
Query = new float[] { 0.01f, 0.45f, 0.67f },
Using = "dense",
Limit = 100
},
},
Query = new Rrf(),
Limit = 100
},
],
query: new Formula
{
Expression = new SumExpression
{
Sum =
{
"$score", // the fused score from the RRF prefetch
new MultExpression
{
Mult =
{
0.1f, // caps decay contribution
Expression.FromExpDecay(
new()
{
X = Expression.FromDateTimeKey("published_at"),
Target = Expression.FromDateTime("YYYY-MM-DDT00:00:00Z"),
Scale = 86400 * 180, // 180 days in seconds
Midpoint = 0.5f
}
)
}
}
}
}
},
limit: 10
);
}
}
@@ -0,0 +1,56 @@
```csharp
using Qdrant.Client;
using Qdrant.Client.Grpc;
var client = new QdrantClient("localhost", 6334);
await client.QueryAsync(
collectionName: "{collection_name}",
prefetch:
[
new PrefetchQuery {
Prefetch = {
new PrefetchQuery {
Query = new(float, uint)[] { (0.22f, 1), (0.8f, 42) },
Using = "sparse",
Limit = 100
},
new PrefetchQuery {
Query = new float[] { 0.01f, 0.45f, 0.67f },
Using = "dense",
Limit = 100
},
},
Query = new Rrf(),
Limit = 100
},
],
query: new Formula
{
Expression = new SumExpression
{
Sum =
{
"$score", // the fused score from the RRF prefetch
new MultExpression
{
Mult =
{
0.1f, // caps decay contribution
Expression.FromExpDecay(
new()
{
X = Expression.FromDateTimeKey("published_at"),
Target = Expression.FromDateTime("YYYY-MM-DDT00:00:00Z"),
Scale = 86400 * 180, // 180 days in seconds
Midpoint = 0.5f
}
)
}
}
}
}
},
limit: 10
);
```
@@ -0,0 +1,53 @@
```go
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Prefetch: []*qdrant.PrefetchQuery{
{
Prefetch: []*qdrant.PrefetchQuery{
{
Query: qdrant.NewQuerySparse([]uint32{1, 42}, []float32{0.22, 0.8}),
Using: qdrant.PtrOf("sparse"),
Limit: qdrant.PtrOf(uint64(100)),
},
{
Query: qdrant.NewQueryDense([]float32{0.01, 0.45, 0.67}),
Using: qdrant.PtrOf("dense"),
Limit: qdrant.PtrOf(uint64(100)),
},
},
Query: qdrant.NewQueryRRF(&qdrant.Rrf{}),
Limit: qdrant.PtrOf(uint64(100)),
},
},
Query: qdrant.NewQueryFormula(&qdrant.Formula{
Expression: qdrant.NewExpressionSum(&qdrant.SumExpression{
Sum: []*qdrant.Expression{
qdrant.NewExpressionVariable("$score"), // the fused score from the RRF prefetch
qdrant.NewExpressionMult(&qdrant.MultExpression{
Mult: []*qdrant.Expression{
qdrant.NewExpressionConstant(0.1), // caps decay contribution
qdrant.NewExpressionExpDecay(&qdrant.DecayParamsExpression{
X: qdrant.NewExpressionDatetimeKey("published_at"),
Target: qdrant.NewExpressionDatetime("YYYY-MM-DDT00:00:00Z"),
Scale: qdrant.PtrOf(float32(86400 * 180)), // 180 days in seconds
Midpoint: qdrant.PtrOf(float32(0.5)),
}),
},
}),
},
}),
}),
Limit: qdrant.PtrOf(uint64(10)),
})
```
@@ -0,0 +1,73 @@
```java
import static io.qdrant.client.ExpressionFactory.constant;
import static io.qdrant.client.ExpressionFactory.datetime;
import static io.qdrant.client.ExpressionFactory.datetimeKey;
import static io.qdrant.client.ExpressionFactory.expDecay;
import static io.qdrant.client.ExpressionFactory.mult;
import static io.qdrant.client.ExpressionFactory.sum;
import static io.qdrant.client.ExpressionFactory.variable;
import static io.qdrant.client.QueryFactory.formula;
import static io.qdrant.client.QueryFactory.nearest;
import static io.qdrant.client.QueryFactory.rrf;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.DecayParamsExpression;
import io.qdrant.client.grpc.Points.Formula;
import io.qdrant.client.grpc.Points.MultExpression;
import io.qdrant.client.grpc.Points.PrefetchQuery;
import io.qdrant.client.grpc.Points.QueryPoints;
import io.qdrant.client.grpc.Points.Rrf;
import io.qdrant.client.grpc.Points.SumExpression;
import java.util.List;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName("{collection_name}")
.addPrefetch(
PrefetchQuery.newBuilder()
.addPrefetch(
PrefetchQuery.newBuilder()
.setQuery(nearest(List.of(0.22f, 0.8f), List.of(1, 42)))
.setUsing("sparse")
.setLimit(100)
.build())
.addPrefetch(
PrefetchQuery.newBuilder()
.setQuery(nearest(List.of(0.01f, 0.45f, 0.67f)))
.setUsing("dense")
.setLimit(100)
.build())
.setQuery(rrf(Rrf.newBuilder().build()))
.setLimit(100)
.build())
.setQuery(
formula(
Formula.newBuilder()
.setExpression(
sum(
SumExpression.newBuilder()
.addSum(variable("$score"))
.addSum(
mult(
MultExpression.newBuilder()
.addMult(constant(0.1f))
.addMult(
expDecay(
DecayParamsExpression.newBuilder()
.setX(datetimeKey("published_at"))
.setTarget(
datetime("YYYY-MM-DDT00:00:00Z"))
.setScale(86400 * 180)
.setMidpoint(0.5f)
.build()))
.build()))
.build()))
.build()))
.setLimit(10)
.build())
.get();
```
@@ -0,0 +1,44 @@
```python
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.query_points(
collection_name="{collection_name}",
prefetch=models.Prefetch(
prefetch=[
models.Prefetch(
query=models.SparseVector(indices=[1, 42], values=[0.22, 0.8]),
using="sparse",
limit=100,
),
models.Prefetch(
query=[0.01, 0.45, 0.67], # <-- dense vector
using="dense",
limit=100,
),
],
query=models.RrfQuery(rrf=models.Rrf()),
limit=100,
),
query=models.FormulaQuery(
formula=models.SumExpression(
sum=[
"$score", # the fused score from the RRF prefetch
models.MultExpression(mult=[
0.1, # caps decay contribution; un-weighted decay [0, 1] would otherwise crowd out small RRF scores
models.ExpDecayExpression(
exp_decay=models.DecayParamsExpression(
x=models.DatetimeKeyExpression(datetime_key="published_at"),
target=models.DatetimeExpression(datetime="YYYY-MM-DDT00:00:00Z"),
scale=86400 * 180, # 180 days in seconds
midpoint=0.5,
)
),
]),
]
)
),
limit=10,
)
```
@@ -0,0 +1,46 @@
```rust
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{
DecayParamsExpressionBuilder, Expression, FormulaBuilder, PrefetchQueryBuilder, Query,
QueryPointsBuilder, RrfBuilder,
};
let client = Qdrant::from_url("http://localhost:6334").build()?;
client.query(
QueryPointsBuilder::new("{collection_name}")
.add_prefetch(
PrefetchQueryBuilder::default()
.add_prefetch(
PrefetchQueryBuilder::default()
.query(Query::new_nearest([(1, 0.22), (42, 0.8)].as_slice()))
.using("sparse")
.limit(100u64),
)
.add_prefetch(
PrefetchQueryBuilder::default()
.query(Query::new_nearest(vec![0.01, 0.45, 0.67]))
.using("dense")
.limit(100u64),
)
.query(Query::new_rrf(RrfBuilder::default()))
.limit(100u64),
)
.query(
FormulaBuilder::new(Expression::sum_with([
Expression::score(),
Expression::mult_with([
Expression::constant(0.1),
Expression::exp_decay(
DecayParamsExpressionBuilder::new(Expression::datetime_key("published_at"))
.target(Expression::datetime("YYYY-MM-DDT00:00:00Z"))
.scale(86400.0 * 180.0)
.midpoint(0.5),
),
]),
])),
)
.limit(10u64),
)
.await?;
```
@@ -0,0 +1,48 @@
```typescript
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
await client.query("{collection_name}", {
prefetch: {
prefetch: [
{
query: {
values: [0.22, 0.8],
indices: [1, 42],
},
using: "sparse",
limit: 100,
},
{
query: [0.01, 0.45, 0.67], // <-- dense vector
using: "dense",
limit: 100,
},
],
query: { rrf: {} },
limit: 100,
},
query: {
formula: {
sum: [
"$score", // the fused score from the RRF prefetch
{
mult: [
0.1, // caps decay contribution; un-weighted decay [0, 1] would otherwise crowd out small RRF scores
{
exp_decay: {
x: { datetime_key: "published_at" },
target: { datetime: "YYYY-MM-DDT00:00:00Z" },
scale: 86400 * 180, // 180 days in seconds
midpoint: 0.5,
},
},
],
},
],
},
},
limit: 10,
});
```
@@ -0,0 +1,57 @@
package snippet
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
func Main() {
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
if err != nil { panic(err) } // @hide
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Prefetch: []*qdrant.PrefetchQuery{
{
Prefetch: []*qdrant.PrefetchQuery{
{
Query: qdrant.NewQuerySparse([]uint32{1, 42}, []float32{0.22, 0.8}),
Using: qdrant.PtrOf("sparse"),
Limit: qdrant.PtrOf(uint64(100)),
},
{
Query: qdrant.NewQueryDense([]float32{0.01, 0.45, 0.67}),
Using: qdrant.PtrOf("dense"),
Limit: qdrant.PtrOf(uint64(100)),
},
},
Query: qdrant.NewQueryRRF(&qdrant.Rrf{}),
Limit: qdrant.PtrOf(uint64(100)),
},
},
Query: qdrant.NewQueryFormula(&qdrant.Formula{
Expression: qdrant.NewExpressionSum(&qdrant.SumExpression{
Sum: []*qdrant.Expression{
qdrant.NewExpressionVariable("$score"), // the fused score from the RRF prefetch
qdrant.NewExpressionMult(&qdrant.MultExpression{
Mult: []*qdrant.Expression{
qdrant.NewExpressionConstant(0.1), // caps decay contribution
qdrant.NewExpressionExpDecay(&qdrant.DecayParamsExpression{
X: qdrant.NewExpressionDatetimeKey("published_at"),
Target: qdrant.NewExpressionDatetime("YYYY-MM-DDT00:00:00Z"),
Scale: qdrant.PtrOf(float32(86400 * 180)), // 180 days in seconds
Midpoint: qdrant.PtrOf(float32(0.5)),
}),
},
}),
},
}),
}),
Limit: qdrant.PtrOf(uint64(10)),
})
}
@@ -0,0 +1,49 @@
```http
POST /collections/{collection_name}/points/query
{
"prefetch": {
"prefetch": [
{
"query": {
"indices": [1, 42], // <┐
"values": [0.22, 0.8] // <┴─sparse vector
},
"using": "sparse",
"limit": 100
},
{
"query": [0.01, 0.45, 0.67, ...], // <-- dense vector
"using": "dense",
"limit": 100
}
],
"query": { "rrf": {} },
"limit": 100
},
"query": {
"formula": {
"sum": [
"$score", // the fused score from the RRF prefetch
{
"mult": [
0.1, // caps decay contribution
{
"exp_decay": {
"x": {
"datetime_key": "published_at"
},
"target": {
"datetime": "YYYY-MM-DDT00:00:00Z"
},
"scale": 15552000, // 180 days in seconds
"midpoint": 0.5
}
}
]
}
]
}
},
"limit": 10
}
```
@@ -0,0 +1,77 @@
package com.example.snippets_amalgamation;
import static io.qdrant.client.ExpressionFactory.constant;
import static io.qdrant.client.ExpressionFactory.datetime;
import static io.qdrant.client.ExpressionFactory.datetimeKey;
import static io.qdrant.client.ExpressionFactory.expDecay;
import static io.qdrant.client.ExpressionFactory.mult;
import static io.qdrant.client.ExpressionFactory.sum;
import static io.qdrant.client.ExpressionFactory.variable;
import static io.qdrant.client.QueryFactory.formula;
import static io.qdrant.client.QueryFactory.nearest;
import static io.qdrant.client.QueryFactory.rrf;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.DecayParamsExpression;
import io.qdrant.client.grpc.Points.Formula;
import io.qdrant.client.grpc.Points.MultExpression;
import io.qdrant.client.grpc.Points.PrefetchQuery;
import io.qdrant.client.grpc.Points.QueryPoints;
import io.qdrant.client.grpc.Points.Rrf;
import io.qdrant.client.grpc.Points.SumExpression;
import java.util.List;
public class Snippet {
public static void run() throws Exception {
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName("{collection_name}")
.addPrefetch(
PrefetchQuery.newBuilder()
.addPrefetch(
PrefetchQuery.newBuilder()
.setQuery(nearest(List.of(0.22f, 0.8f), List.of(1, 42)))
.setUsing("sparse")
.setLimit(100)
.build())
.addPrefetch(
PrefetchQuery.newBuilder()
.setQuery(nearest(List.of(0.01f, 0.45f, 0.67f)))
.setUsing("dense")
.setLimit(100)
.build())
.setQuery(rrf(Rrf.newBuilder().build()))
.setLimit(100)
.build())
.setQuery(
formula(
Formula.newBuilder()
.setExpression(
sum(
SumExpression.newBuilder()
.addSum(variable("$score"))
.addSum(
mult(
MultExpression.newBuilder()
.addMult(constant(0.1f))
.addMult(
expDecay(
DecayParamsExpression.newBuilder()
.setX(datetimeKey("published_at"))
.setTarget(
datetime("YYYY-MM-DDT00:00:00Z"))
.setScale(86400 * 180)
.setMidpoint(0.5f)
.build()))
.build()))
.build()))
.build()))
.setLimit(10)
.build())
.get();
}
}
@@ -0,0 +1,42 @@
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.query_points(
collection_name="{collection_name}",
prefetch=models.Prefetch(
prefetch=[
models.Prefetch(
query=models.SparseVector(indices=[1, 42], values=[0.22, 0.8]),
using="sparse",
limit=100,
),
models.Prefetch(
query=[0.01, 0.45, 0.67], # <-- dense vector
using="dense",
limit=100,
),
],
query=models.RrfQuery(rrf=models.Rrf()),
limit=100,
),
query=models.FormulaQuery(
formula=models.SumExpression(
sum=[
"$score", # the fused score from the RRF prefetch
models.MultExpression(mult=[
0.1, # caps decay contribution; un-weighted decay [0, 1] would otherwise crowd out small RRF scores
models.ExpDecayExpression(
exp_decay=models.DecayParamsExpression(
x=models.DatetimeKeyExpression(datetime_key="published_at"),
target=models.DatetimeExpression(datetime="YYYY-MM-DDT00:00:00Z"),
scale=86400 * 180, # 180 days in seconds
midpoint=0.5,
)
),
]),
]
)
),
limit=10,
)
@@ -0,0 +1,48 @@
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{
DecayParamsExpressionBuilder, Expression, FormulaBuilder, PrefetchQueryBuilder, Query,
QueryPointsBuilder, RrfBuilder,
};
pub async fn main() -> anyhow::Result<()> {
let client = Qdrant::from_url("http://localhost:6334").build()?;
client.query(
QueryPointsBuilder::new("{collection_name}")
.add_prefetch(
PrefetchQueryBuilder::default()
.add_prefetch(
PrefetchQueryBuilder::default()
.query(Query::new_nearest([(1, 0.22), (42, 0.8)].as_slice()))
.using("sparse")
.limit(100u64),
)
.add_prefetch(
PrefetchQueryBuilder::default()
.query(Query::new_nearest(vec![0.01, 0.45, 0.67]))
.using("dense")
.limit(100u64),
)
.query(Query::new_rrf(RrfBuilder::default()))
.limit(100u64),
)
.query(
FormulaBuilder::new(Expression::sum_with([
Expression::score(),
Expression::mult_with([
Expression::constant(0.1),
Expression::exp_decay(
DecayParamsExpressionBuilder::new(Expression::datetime_key("published_at"))
.target(Expression::datetime("YYYY-MM-DDT00:00:00Z"))
.scale(86400.0 * 180.0)
.midpoint(0.5),
),
]),
])),
)
.limit(10u64),
)
.await?;
Ok(())
}
@@ -0,0 +1,46 @@
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
await client.query("{collection_name}", {
prefetch: {
prefetch: [
{
query: {
values: [0.22, 0.8],
indices: [1, 42],
},
using: "sparse",
limit: 100,
},
{
query: [0.01, 0.45, 0.67], // <-- dense vector
using: "dense",
limit: 100,
},
],
query: { rrf: {} },
limit: 100,
},
query: {
formula: {
sum: [
"$score", // the fused score from the RRF prefetch
{
mult: [
0.1, // caps decay contribution; un-weighted decay [0, 1] would otherwise crowd out small RRF scores
{
exp_decay: {
x: { datetime_key: "published_at" },
target: { datetime: "YYYY-MM-DDT00:00:00Z" },
scale: 86400 * 180, // 180 days in seconds
midpoint: 0.5,
},
},
],
},
],
},
},
limit: 10,
});
@@ -25,7 +25,7 @@ public class Snippet
Limit = 20
}
},
query: Fusion.Rrf
query: new Rrf()
);
}
}
@@ -22,6 +22,6 @@ await client.QueryAsync(
Limit = 20
}
},
query: Fusion.Rrf
query: new Rrf()
);
```
@@ -16,12 +16,14 @@ client.Query(context.Background(), &qdrant.QueryPoints{
{
Query: qdrant.NewQuerySparse([]uint32{1, 42}, []float32{0.22, 0.8}),
Using: qdrant.PtrOf("sparse"),
Limit: qdrant.PtrOf(uint64(20)),
},
{
Query: qdrant.NewQueryDense([]float32{0.01, 0.45, 0.67}),
Using: qdrant.PtrOf("dense"),
Limit: qdrant.PtrOf(uint64(20)),
},
},
Query: qdrant.NewQueryFusion(qdrant.Fusion_RRF),
Query: qdrant.NewQueryRRF(&qdrant.Rrf{}),
})
```
@@ -1,12 +1,12 @@
```java
import static io.qdrant.client.QueryFactory.fusion;
import static io.qdrant.client.QueryFactory.nearest;
import static io.qdrant.client.QueryFactory.rrf;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Fusion;
import io.qdrant.client.grpc.Points.PrefetchQuery;
import io.qdrant.client.grpc.Points.QueryPoints;
import io.qdrant.client.grpc.Points.Rrf;
import java.util.List;
QdrantClient client = new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
@@ -24,7 +24,7 @@ client.queryAsync(
.setUsing("dense")
.setLimit(20)
.build())
.setQuery(fusion(Fusion.RRF))
.setQuery(rrf(Rrf.newBuilder().build()))
.build())
.get();
```
@@ -17,6 +17,6 @@ client.query_points(
limit=20,
),
],
query=models.FusionQuery(fusion=models.Fusion.RRF),
query=models.RrfQuery(rrf=models.Rrf()),
)
```
@@ -1,6 +1,6 @@
```rust
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{Fusion, PrefetchQueryBuilder, Query, QueryPointsBuilder};
use qdrant_client::qdrant::{PrefetchQueryBuilder, Query, QueryPointsBuilder, RrfBuilder};
let client = Qdrant::from_url("http://localhost:6334").build()?;
@@ -16,6 +16,6 @@ client.query(
.using("dense")
.limit(20u64)
)
.query(Query::new_fusion(Fusion::Rrf))
.query(Query::new_rrf(RrfBuilder::default()))
).await?;
```
@@ -20,7 +20,7 @@ client.query("{collection_name}", {
},
],
query: {
fusion: 'rrf',
rrf: {},
},
});
```
@@ -20,12 +20,14 @@ func Main() {
{
Query: qdrant.NewQuerySparse([]uint32{1, 42}, []float32{0.22, 0.8}),
Using: qdrant.PtrOf("sparse"),
Limit: qdrant.PtrOf(uint64(20)),
},
{
Query: qdrant.NewQueryDense([]float32{0.01, 0.45, 0.67}),
Using: qdrant.PtrOf("dense"),
Limit: qdrant.PtrOf(uint64(20)),
},
},
Query: qdrant.NewQueryFusion(qdrant.Fusion_RRF),
Query: qdrant.NewQueryRRF(&qdrant.Rrf{}),
})
}
@@ -16,7 +16,7 @@ POST /collections/{collection_name}/points/query
"limit": 20
}
],
"query": { "fusion": "rrf" }, // <--- reciprocal rank fusion
"query": { "rrf": {} }, // <--- reciprocal rank fusion with defaults
"limit": 10
}
```
@@ -1,13 +1,13 @@
package com.example.snippets_amalgamation;
import static io.qdrant.client.QueryFactory.fusion;
import static io.qdrant.client.QueryFactory.nearest;
import static io.qdrant.client.QueryFactory.rrf;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Fusion;
import io.qdrant.client.grpc.Points.PrefetchQuery;
import io.qdrant.client.grpc.Points.QueryPoints;
import io.qdrant.client.grpc.Points.Rrf;
import java.util.List;
public class Snippet {
@@ -27,7 +27,7 @@ public class Snippet {
.setUsing("dense")
.setLimit(20)
.build())
.setQuery(fusion(Fusion.RRF))
.setQuery(rrf(Rrf.newBuilder().build()))
.build())
.get();
}
@@ -16,5 +16,5 @@ client.query_points(
limit=20,
),
],
query=models.FusionQuery(fusion=models.Fusion.RRF),
query=models.RrfQuery(rrf=models.Rrf()),
)
@@ -1,5 +1,5 @@
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{Fusion, PrefetchQueryBuilder, Query, QueryPointsBuilder};
use qdrant_client::qdrant::{PrefetchQueryBuilder, Query, QueryPointsBuilder, RrfBuilder};
pub async fn main() -> anyhow::Result<()> {
let client = Qdrant::from_url("http://localhost:6334").build()?;
@@ -16,7 +16,7 @@ pub async fn main() -> anyhow::Result<()> {
.using("dense")
.limit(20u64)
)
.query(Query::new_fusion(Fusion::Rrf))
.query(Query::new_rrf(RrfBuilder::default()))
).await?;
Ok(())
@@ -19,6 +19,6 @@ client.query("{collection_name}", {
},
],
query: {
fusion: 'rrf',
rrf: {},
},
});