liangkaiwen commented on code in PR #4919:
URL: https://github.com/apache/solr/pull/4919#discussion_r4364026396
##########
solr/core/src/test/org/apache/solr/search/vector/KnnQParserTest.java:
##########
@@ -197,6 +197,270 @@ public void efSearchScaleFactorSet_shouldWorkCorrectly() {
"//result/doc[5]/str[@name='id'][.='3']");
}
+ @Test
+ public void incorrectOversample_shouldThrowException() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ assertQEx(
+ "String rerankOversample should throw Exception",
+ "For input string: \"string\"",
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=string}" + vectorToSearch,
+ "fl",
+ "id"),
+ SolrException.ErrorCode.BAD_REQUEST);
+
+ assertQEx(
+ "Double rerankOversample should throw Exception",
+ "For input string: \"2.5\"",
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=2.5}" + vectorToSearch,
+ "fl",
+ "id"),
+ SolrException.ErrorCode.BAD_REQUEST);
+ }
+
+ @Test
+ public void rerankOversampleLessThanOne_shouldThrowException() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ assertQEx(
+ "rerankOversample = 0 should throw Exception",
+ "rerankOversample (0) must be >= 1",
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=0}" + vectorToSearch,
+ "fl",
+ "id"),
+ SolrException.ErrorCode.BAD_REQUEST);
+
+ assertQEx(
+ "Negative rerankOversample should throw Exception",
+ "rerankOversample (-1) must be >= 1",
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=-1}" + vectorToSearch,
+ "fl",
+ "id"),
+ SolrException.ErrorCode.BAD_REQUEST);
+ }
+
+ @Test
+ public void rerankOversampleOverflowingTopK_shouldThrowException() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ assertQEx(
+ "topK * rerankOversample overflowing an integer should throw
Exception",
+ "topK (2000000000) * rerankOversample (3) overflows an integer",
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=2000000000 rerankOversample=3}" +
vectorToSearch,
+ "fl",
+ "id"),
+ SolrException.ErrorCode.BAD_REQUEST);
+ }
+
+ @Test
+ public void rerankOversampleOnByteEncodedField_shouldThrowException() {
+ String vectorToSearch = "[1, 2, 3, 4]";
+
+ assertQEx(
+ "rerankOversample on a BYTE encoded field should throw Exception",
+ "rerankOversample is only supported for FLOAT32 vector encoding; field
'vector_byte_encoding' uses BYTE",
+ req(
+ CommonParams.Q,
+ "{!knn f=vector_byte_encoding topK=3 rerankOversample=2}" +
vectorToSearch,
+ "fl",
+ "id"),
+ SolrException.ErrorCode.BAD_REQUEST);
+ }
+
+ @Test
+ public void rerankOversampleOneOnByteEncodedField_shouldNotThrow() {
+ String vectorToSearch = "[1, 2, 3, 4]";
+
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector_byte_encoding topK=3 rerankOversample=1}" +
vectorToSearch,
+ "fl",
+ "id"),
+ "//result[@numFound='3']");
+ }
+
+ @Test
+ public void rerankOversampleSet_shouldReturnTopKResults() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ // rerankOversample widens the candidate pool, but exactly topK results
are returned
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=3}" + vectorToSearch,
+ "fl",
+ "id"),
+ "//result[@numFound='5']",
+ "//result/doc[1]/str[@name='id'][.='1']",
+ "//result/doc[2]/str[@name='id'][.='4']",
+ "//result/doc[3]/str[@name='id'][.='2']",
+ "//result/doc[4]/str[@name='id'][.='10']",
+ "//result/doc[5]/str[@name='id'][.='3']");
+ }
+
+ @Test
+ public void rerankOversampleOnNonQuantizedField_shouldNotChangeRanking() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ // 'vector' is not quantized, so the knn search already scores against the
raw vectors and
+ // re-ranking them cannot reorder anything: oversampling must be a no-op
on the final ranking
+ String[] expected =
+ new String[] {
+ "//result[@numFound='5']",
+ "//result/doc[1]/str[@name='id'][.='1']",
+ "//result/doc[2]/str[@name='id'][.='4']",
+ "//result/doc[3]/str[@name='id'][.='2']",
+ "//result/doc[4]/str[@name='id'][.='10']",
+ "//result/doc[5]/str[@name='id'][.='3']"
+ };
+
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=1}" + vectorToSearch,
+ "fl",
+ "id"),
+ expected);
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=4}" + vectorToSearch,
+ "fl",
+ "id"),
+ expected);
+ }
+
+ @Test
+ public void rerankOversampleWithPreFilter_shouldReturnTopKFilteredResults() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=4 rerankOversample=3 preFilter='id:(1 4 7 8 9
10)'}"
+ + vectorToSearch,
+ "fl",
+ "id"),
+ "//result[@numFound='4']",
+ "//result/doc[1]/str[@name='id'][.='1']",
+ "//result/doc[2]/str[@name='id'][.='4']",
+ "//result/doc[3]/str[@name='id'][.='10']",
+ "//result/doc[4]/str[@name='id'][.='7']");
+ }
+
+ @Test
+ public void rerankOversampleWithSeedQuery_shouldReturnTopKResults() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=4 rerankOversample=3 seedQuery='id:(1 4 7 8
9)'}" + vectorToSearch,
+ "fl",
+ "id"),
+ "//result[@numFound='4']",
+ "//result/doc[1]/str[@name='id'][.='1']",
+ "//result/doc[2]/str[@name='id'][.='4']",
+ "//result/doc[3]/str[@name='id'][.='2']",
+ "//result/doc[4]/str[@name='id'][.='10']");
+ }
+
+ @Test
+ public void rerankOversampleWithEarlyTermination_shouldReturnTopKResults() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=3 earlyTermination=true
saturationThreshold=0.989 patience=10}"
+ + vectorToSearch,
+ "fl",
+ "id"),
+ "//result[@numFound='5']");
+ }
+
+ @Test
+ public void rerankOversampleWithFilterMatchingNoDocs_shouldReturnNoResults()
{
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ // A plain fq is folded into the knn query's pre-filter, and a filter
matching nothing makes
+ // the knn query rewrite to MatchNoDocsQuery. The re-ranking wrapper has
to cope with an empty
+ // candidate set rather than failing.
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=3}" + vectorToSearch,
+ "fq",
+ "id:nonexistent",
+ "fl",
+ "id"),
+ "//result[@numFound='0']");
+ }
+
+ @Test
+ public void rerankOversampleWithDebugQuery_matchingNoDocs_shouldNotThrow() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ // debugQuery stringifies the parsed query. The re-ranking query resolves
its similarity
+ // function lazily during the search, which never happens when nothing
matches, so the
+ // similarity function has to be set up front or toString blows up here.
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=3
preFilter='id:nonexistent'}" + vectorToSearch,
+ "fl",
+ "id",
+ CommonParams.DEBUG_QUERY,
+ "true"),
+ "//result[@numFound='0']",
+ "//str[@name='parsedquery_toString']");
+ }
+
+ @Test
+ public void rerankOversampleWithDebugQuery_shouldNotThrow() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=3}" + vectorToSearch,
+ "fl",
+ "id",
+ CommonParams.DEBUG_QUERY,
+ "true"),
+ "//result[@numFound='5']",
+ "//str[@name='parsedquery_toString']");
+ }
+
+ @Test
+ public void
rerankOversampleWithEfSearchScaleFactor_shouldReturnTopKResults() {
+ String vectorToSearch = "[1.0, 2.0, 3.0, 4.0]";
+
+ assertQ(
+ req(
+ CommonParams.Q,
+ "{!knn f=vector topK=5 rerankOversample=2
efSearchScaleFactor=2.0}" + vectorToSearch,
+ "fl",
+ "id"),
+ "//result[@numFound='5']",
+ "//result/doc[1]/str[@name='id'][.='1']",
+ "//result/doc[2]/str[@name='id'][.='4']",
+ "//result/doc[3]/str[@name='id'][.='2']",
+ "//result/doc[4]/str[@name='id'][.='10']",
+ "//result/doc[5]/str[@name='id'][.='3']");
+ }
+
Review Comment:
Deleted the extra test class and consolidated down the tests to those that
are meaningful
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]