CombinersTest.java
package redis.clients.jedis.search.hybrid;
import static org.hamcrest.MatcherAssert.assertThat;
import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.ALPHA;
import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.BETA;
import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.CONSTANT;
import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.WINDOW;
import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.YIELD_SCORE_AS;
import static redis.clients.jedis.util.CommandArgumentsMatchers.hasArgumentCount;
import static redis.clients.jedis.util.CommandArgumentsMatchers.hasArguments;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.CommandArguments;
import redis.clients.jedis.args.RawableFactory;
import redis.clients.jedis.search.Combiner;
import redis.clients.jedis.search.Combiners;
import redis.clients.jedis.search.SearchProtocol.SearchCommand;
/**
* Serialization tests for the FT.HYBRID score {@link Combiner}s built via {@link Combiners}.
* <p>
* Each combiner is serialized in isolation into a {@link CommandArguments} seeded with the HYBRID
* command; the assertions cover only the combiner's own clause ({@code <method> <count> ...}).
* </p>
*/
public class CombinersTest {
private static CommandArguments combineArgs(Combiner combiner) {
CommandArguments args = new CommandArguments(SearchCommand.HYBRID);
combiner.addParams(args);
return args;
}
@Nested
class RrfTests {
@Test
public void rrfWithNoParamsWritesZeroCount() {
CommandArguments args = combineArgs(Combiners.rrf());
// Expected: RRF 0
assertThat(args, hasArgumentCount(3));
assertThat(args,
hasArguments(SearchCommand.HYBRID, RawableFactory.from("RRF"), RawableFactory.from(0)));
}
@Test
public void rrfWithWindowOnly() {
CommandArguments args = combineArgs(Combiners.rrf().window(20));
// Expected: RRF 2 WINDOW 20
assertThat(args, hasArgumentCount(5));
assertThat(args, hasArguments(SearchCommand.HYBRID, RawableFactory.from("RRF"),
RawableFactory.from(2), WINDOW, RawableFactory.from(20)));
}
@Test
public void rrfWithConstantOnly() {
CommandArguments args = combineArgs(Combiners.rrf().constant(60));
// Expected: RRF 2 CONSTANT 60
assertThat(args, hasArgumentCount(5));
assertThat(args, hasArguments(SearchCommand.HYBRID, RawableFactory.from("RRF"),
RawableFactory.from(2), CONSTANT, RawableFactory.from(60.0)));
}
@Test
public void rrfWithWindowAndConstant() {
CommandArguments args = combineArgs(Combiners.rrf().window(20).constant(60));
// Expected: RRF 4 WINDOW 20 CONSTANT 60
assertThat(args, hasArgumentCount(7));
assertThat(args,
hasArguments(SearchCommand.HYBRID, RawableFactory.from("RRF"), RawableFactory.from(4),
WINDOW, RawableFactory.from(20), CONSTANT, RawableFactory.from(60.0)));
}
}
@Nested
class LinearTests {
@Test
public void linearWithNoParamsWritesZeroCount() {
CommandArguments args = combineArgs(Combiners.linear());
// Expected: LINEAR 0
assertThat(args, hasArgumentCount(3));
assertThat(args,
hasArguments(SearchCommand.HYBRID, RawableFactory.from("LINEAR"), RawableFactory.from(0)));
}
@Test
public void linearWithAlphaOnly() {
CommandArguments args = combineArgs(Combiners.linear().alpha(0.7));
// Expected: LINEAR 2 ALPHA 0.7
assertThat(args, hasArgumentCount(5));
assertThat(args, hasArguments(SearchCommand.HYBRID, RawableFactory.from("LINEAR"),
RawableFactory.from(2), ALPHA, RawableFactory.from(0.7)));
}
@Test
public void linearWithBetaOnly() {
CommandArguments args = combineArgs(Combiners.linear().beta(0.3));
// Expected: LINEAR 2 BETA 0.3
assertThat(args, hasArgumentCount(5));
assertThat(args, hasArguments(SearchCommand.HYBRID, RawableFactory.from("LINEAR"),
RawableFactory.from(2), BETA, RawableFactory.from(0.3)));
}
@Test
public void linearWithWindowOnly() {
CommandArguments args = combineArgs(Combiners.linear().window(25));
// Expected: LINEAR 2 WINDOW 25
assertThat(args, hasArgumentCount(5));
assertThat(args, hasArguments(SearchCommand.HYBRID, RawableFactory.from("LINEAR"),
RawableFactory.from(2), WINDOW, RawableFactory.from(25)));
}
@Test
public void linearWithAlphaAndBeta() {
CommandArguments args = combineArgs(Combiners.linear().alpha(0.7).beta(0.3));
// Expected: LINEAR 4 ALPHA 0.7 BETA 0.3
assertThat(args, hasArgumentCount(7));
assertThat(args, hasArguments(SearchCommand.HYBRID, RawableFactory.from("LINEAR"),
RawableFactory.from(4), ALPHA, RawableFactory.from(0.7), BETA, RawableFactory.from(0.3)));
}
@Test
public void linearWithAllParamsKeepsAlphaBetaWindowOrder() {
CommandArguments args = combineArgs(Combiners.linear().alpha(0.7).beta(0.3).window(25));
// Expected: LINEAR 6 ALPHA 0.7 BETA 0.3 WINDOW 25
assertThat(args, hasArgumentCount(9));
assertThat(args,
hasArguments(SearchCommand.HYBRID, RawableFactory.from("LINEAR"), RawableFactory.from(6),
ALPHA, RawableFactory.from(0.7), BETA, RawableFactory.from(0.3), WINDOW,
RawableFactory.from(25)));
}
}
/**
* The combined-score alias is appended as {@code YIELD_SCORE_AS <alias>} and must be included in
* the clause count. Regression coverage for the off-by-two count bug where the two alias tokens
* fell outside the count and Redis rejected them as an unknown argument.
*/
@Nested
class YieldScoreAsTests {
@Test
public void rrfWithParamsAndAliasCountsAliasTokens() {
CommandArguments args = combineArgs(Combiners.rrf().window(20).constant(60).as("score"));
// Expected: RRF 6 WINDOW 20 CONSTANT 60 YIELD_SCORE_AS score
assertThat(args, hasArgumentCount(9));
assertThat(args,
hasArguments(SearchCommand.HYBRID, RawableFactory.from("RRF"), RawableFactory.from(6),
WINDOW, RawableFactory.from(20), CONSTANT, RawableFactory.from(60.0), YIELD_SCORE_AS,
RawableFactory.from("score")));
}
@Test
public void rrfWithOnlyAliasCountsAliasTokens() {
CommandArguments args = combineArgs(Combiners.rrf().as("score"));
// Expected: RRF 2 YIELD_SCORE_AS score
assertThat(args, hasArgumentCount(5));
assertThat(args, hasArguments(SearchCommand.HYBRID, RawableFactory.from("RRF"),
RawableFactory.from(2), YIELD_SCORE_AS, RawableFactory.from("score")));
}
@Test
public void linearWithParamsAndAliasCountsAliasTokens() {
CommandArguments args = combineArgs(Combiners.linear().alpha(0.7).beta(0.3).as("score"));
// Expected: LINEAR 6 ALPHA 0.7 BETA 0.3 YIELD_SCORE_AS score
assertThat(args, hasArgumentCount(9));
assertThat(args,
hasArguments(SearchCommand.HYBRID, RawableFactory.from("LINEAR"), RawableFactory.from(6),
ALPHA, RawableFactory.from(0.7), BETA, RawableFactory.from(0.3), YIELD_SCORE_AS,
RawableFactory.from("score")));
}
@Test
public void linearWithOnlyAliasCountsAliasTokens() {
CommandArguments args = combineArgs(Combiners.linear().as("score"));
// Expected: LINEAR 2 YIELD_SCORE_AS score
assertThat(args, hasArgumentCount(5));
assertThat(args, hasArguments(SearchCommand.HYBRID, RawableFactory.from("LINEAR"),
RawableFactory.from(2), YIELD_SCORE_AS, RawableFactory.from("score")));
}
}
}