Skip to content

feat: add searchOperator (minimum_should_match) to bm25 and hybrid arguments #386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package io.weaviate.client.v1.graphql.query.argument;

import java.util.LinkedHashSet;
import java.util.Set;

import io.weaviate.client.v1.graphql.query.util.Serializer;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;

import java.util.LinkedHashSet;
import java.util.Set;

@Getter
@Builder
@ToString
Expand All @@ -19,6 +20,7 @@
public class Bm25Argument implements Argument {
String query;
String[] properties;
SearchOperator searchOperator;

@Override
public String build() {
Expand All @@ -28,7 +30,34 @@ public String build() {
if (properties != null) {
arg.add(String.format("properties:%s", Serializer.arrayWithQuotes(properties)));
}
if (searchOperator != null) {
arg.add(String.format("searchOperator:%s", searchOperator.build()));
}

return String.format("bm25:{%s}", String.join(" ", arg));
}

@AllArgsConstructor(access = AccessLevel.PRIVATE)
public static class SearchOperator implements Argument {
private static final String OR = "Or";
private static final String AND = "And";

private String operator;
private int minimumMatch;

public static SearchOperator and() {
return new SearchOperator(AND, 0); // minimumMatch ignored for And
}

public static SearchOperator or(int minimumMatch) {
return new SearchOperator(OR, minimumMatch);
}

@Override
public String build() {
// While minimumOrTokensMatch is ignored, it should nevertheless be included
// in the query, otherwise the server refuses to execute it.
return String.format("{operator:%s minimumOrTokensMatch:%s}", operator, minimumMatch);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class HybridArgument implements Argument {
String[] targetVectors;
Searches searches;
Targets targets;
Bm25Argument.SearchOperator bm25SearchOperator;

@Override
public String build() {
Expand Down Expand Up @@ -63,6 +64,9 @@ public String build() {
}
arg.add(String.format("searches:{%s}", String.join(" ", searchesArgs)));
}
if (bm25SearchOperator != null) {
arg.add(String.format("bm25SearchOperator:%s", bm25SearchOperator.build()));
}
if (targets != null) {
arg.add(String.format("%s", targets.build()));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package io.weaviate.client.v1.graphql.query.argument;

import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Test;

public class Bm25ArgumentTest {

@Test
public void shouldCreateArgument() {
Bm25Argument bm25 = Bm25Argument.builder()
.query("I'm a simple string")
.build();
.query("I'm a simple string")
.build();

String str = bm25.build();

Expand All @@ -20,26 +20,50 @@ public void shouldCreateArgument() {
@Test
public void shouldCreateArgumentWithProperties() {
Bm25Argument bm25 = Bm25Argument.builder()
.query("I'm a simple string")
.properties(new String[]{"prop1", "prop2"})
.build();
.query("I'm a simple string")
.properties(new String[] { "prop1", "prop2" })
.build();

String str = bm25.build();

assertThat(str).isEqualTo("bm25:{query:\"I'm a simple string\" " +
"properties:[\"prop1\",\"prop2\"]}");
"properties:[\"prop1\",\"prop2\"]}");
}

@Test
public void shouldCreateArgumentWithSearchOperator_And() {
Bm25Argument bm25 = Bm25Argument.builder()
.query("hello")
.searchOperator(Bm25Argument.SearchOperator.and())
.build();

String str = bm25.build();

assertThat(str).isEqualTo("bm25:{query:\"hello\" searchOperator:{operator:And minimumOrTokensMatch:0}}");
}

@Test
public void shouldCreateArgumentWithSearchOperator_Or() {
Bm25Argument bm25 = Bm25Argument.builder()
.query("hello")
.searchOperator(Bm25Argument.SearchOperator.or(2))
.build();

String str = bm25.build();

assertThat(str).isEqualTo("bm25:{query:\"hello\" searchOperator:{operator:Or minimumOrTokensMatch:2}}");
}

@Test
public void shouldCreateArgumentWithChars() {
Bm25Argument bm25 = Bm25Argument.builder()
.query("\"I'm a complex string\" says the {'`:string:`'}")
.properties(new String[]{"prop:\"'`{0}`'\""})
.build();
.query("\"I'm a complex string\" says the {'`:string:`'}")
.properties(new String[] { "prop:\"'`{0}`'\"" })
.build();

String str = bm25.build();

assertThat(str).isEqualTo("bm25:{query:\"\\\"I'm a complex string\\\" says the {'`:string:`'}\" " +
"properties:[\"prop:\\\"'`{0}`'\\\"\"]}");
"properties:[\"prop:\\\"'`{0}`'\\\"\"]}");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,23 @@ public static Object[][] provideTestCases() {
.build())
.build(),
"hybrid:{query:\"ColBERT me if you can!\" searches:{nearVector:{vector:[[1.0,2.0,3.0],[4.0,5.0,6.0]] targetVectors:[\"colbert\"]}}}",
}
},
{
"bm25 search operator And",
HybridArgument.builder()
.query("hello")
.bm25SearchOperator(Bm25Argument.SearchOperator.and())
.build(),
"hybrid:{query:\"hello\" bm25SearchOperator:{operator:And minimumOrTokensMatch:0}}",
},
{
"bm25 search operator Or",
HybridArgument.builder()
.query("hello")
.bm25SearchOperator(Bm25Argument.SearchOperator.or(2))
.build(),
"hybrid:{query:\"hello\" bm25SearchOperator:{operator:Or minimumOrTokensMatch:2}}",
},
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
public class WeaviateVersion {

// docker image version
public static final String WEAVIATE_IMAGE = "1.31.0-rc.0-5ff7495";
public static final String WEAVIATE_IMAGE = "1.31.0";

// to be set according to weaviate docker image
public static final String EXPECTED_WEAVIATE_VERSION = "1.31.0-rc.0";
public static final String EXPECTED_WEAVIATE_VERSION = "1.31.0";
// to be set according to weaviate docker image
public static final String EXPECTED_WEAVIATE_GIT_HASH = "5ff7495";
public static final String EXPECTED_WEAVIATE_GIT_HASH = "79499d6";

private WeaviateVersion() {}
private WeaviateVersion() {
}
}
Loading