/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.ltr;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.ltr.model.LinearModel;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class TestLTRQParserExplain extends TestRerankBase {
@BeforeClass
public static void setup() throws Exception {
setuptest(true);
loadFeatures("features-store-test-model.json");
}
@AfterClass
public static void after() throws Exception {
aftertest();
}
@Test
public void testRerankedExplain() throws Exception {
loadModel("linear2", LinearModel.class.getCanonicalName(), new String[] {
"constant1", "constant2", "pop"},
"{\"weights\":{\"pop\":1.0,\"constant1\":1.5,\"constant2\":3.5}}");
final SolrQuery query = new SolrQuery();
query.setQuery("title:bloomberg");
query.setParam("debugQuery", "on");
query.add("rows", "2");
query.add("rq", "{!ltr reRankDocs=2 model=linear2}");
query.add("fl", "*,score");
assertJQ(
"/query" + query.toQueryString(),
"/debug/explain/9=='\n13.5 = LinearModel(name=linear2,featureWeights=[constant1=1.5,constant2=3.5,pop=1.0]) model applied to features, sum of:\n 1.5 = prod of:\n 1.5 = weight on feature\n 1.0 = ValueFeature [name=constant1, params={value=1}]\n 7.0 = prod of:\n 3.5 = weight on feature\n 2.0 = ValueFeature [name=constant2, params={value=2}]\n 5.0 = prod of:\n 1.0 = weight on feature\n 5.0 = FieldValueFeature [name=pop, params={field=popularity}]\n'");
}
@Test
public void testRerankedExplainSameBetweenDifferentDocsWithSameFeatures() throws Exception {
loadFeatures("features-linear.json");
loadModels("linear-model.json");
final SolrQuery query = new SolrQuery();
query.setQuery("title:bloomberg");
query.setParam("debugQuery", "on");
query.add("rows", "4");
query.add("rq", "{!ltr reRankDocs=4 model=6029760550880411648}");
query.add("fl", "*,score");
query.add("wt", "json");
final String expectedExplainNormalizer = "normalized using MinMaxNormalizer(min=0.0,max=10.0)";
final String expectedExplain = "\n3.5116758 = LinearModel(name=6029760550880411648,featureWeights=["
+ "title=0.0,"
+ "description=0.1,"
+ "keywords=0.2,"
+ "popularity=0.3,"
+ "text=0.4,"
+ "queryIntentPerson=0.1231231,"
+ "queryIntentCompany=0.12121211"
+ "]) model applied to features, sum of:\n 0.0 = prod of:\n 0.0 = weight on feature\n 1.0 = ValueFeature [name=title, params={value=1}]\n 0.2 = prod of:\n 0.1 = weight on feature\n 2.0 = ValueFeature [name=description, params={value=2}]\n 0.4 = prod of:\n 0.2 = weight on feature\n 2.0 = ValueFeature [name=keywords, params={value=2}]\n 0.09 = prod of:\n 0.3 = weight on feature\n 0.3 = "+expectedExplainNormalizer+"\n 3.0 = ValueFeature [name=popularity, params={value=3}]\n 1.6 = prod of:\n 0.4 = weight on feature\n 4.0 = ValueFeature [name=text, params={value=4}]\n 0.6156155 = prod of:\n 0.1231231 = weight on feature\n 5.0 = ValueFeature [name=queryIntentPerson, params={value=5}]\n 0.60606056 = prod of:\n 0.12121211 = weight on feature\n 5.0 = ValueFeature [name=queryIntentCompany, params={value=5}]\n";
assertJQ(
"/query" + query.toQueryString(),
"/debug/explain/7=='"+expectedExplain+"'}");
assertJQ(
"/query" + query.toQueryString(),
"/debug/explain/9=='"+expectedExplain+"'}");
}
@Test
public void LinearScoreExplainMissingEfiFeatureShouldReturnDefaultScore() throws Exception {
loadFeatures("features-linear-efi.json");
loadModels("linear-model-efi.json");
SolrQuery query = new SolrQuery();
query.setQuery("title:bloomberg");
query.setParam("debugQuery", "on");
query.add("rows", "4");
query.add("rq", "{!ltr reRankDocs=4 model=linear-efi}");
query.add("fl", "*,score");
query.add("wt", "xml");
final String linearModelEfiString = "LinearModel(name=linear-efi,featureWeights=["
+ "sampleConstant=1.0,"
+ "search_number_of_nights=2.0])";
query.remove("wt");
query.add("wt", "json");
assertJQ(
"/query" + query.toQueryString(),
"/debug/explain/7=='\n5.0 = "+linearModelEfiString+" model applied to features, sum of:\n 5.0 = prod of:\n 1.0 = weight on feature\n 5.0 = ValueFeature [name=sampleConstant, params={value=5}]\n" +
" 0.0 = prod of:\n" +
" 2.0 = weight on feature\n" +
" 0.0 = The feature has no value\n'}");
assertJQ(
"/query" + query.toQueryString(),
"/debug/explain/9=='\n5.0 = "+linearModelEfiString+" model applied to features, sum of:\n 5.0 = prod of:\n 1.0 = weight on feature\n 5.0 = ValueFeature [name=sampleConstant, params={value=5}]\n" +
" 0.0 = prod of:\n" +
" 2.0 = weight on feature\n" +
" 0.0 = The feature has no value\n'}");
}
@Test
public void multipleAdditiveTreesScoreExplainMissingEfiFeatureShouldReturnDefaultScore() throws Exception {
loadFeatures("external_features_for_sparse_processing.json");
loadModels("multipleadditivetreesmodel_external_binary_features.json");
SolrQuery query = new SolrQuery();
query.setQuery("title:bloomberg");
query.setParam("debugQuery", "on");
query.add("rows", "4");
query.add("rq", "{!ltr reRankDocs=4 model=external_model_binary_feature efi.user_device_tablet=1}");
query.add("fl", "*,score");
final String tree1 = "(weight=1.0,root=(feature=user_device_smartphone,threshold=0.5,left=0.0,right=50.0))";
final String tree2 = "(weight=1.0,root=(feature=user_device_tablet,threshold=0.5,left=0.0,right=65.0))";
final String trees = "["+tree1+","+tree2+"]";
query.add("wt", "json");
assertJQ(
"/query" + query.toQueryString(),
"/debug/explain/7=='\n" +
"65.0 = MultipleAdditiveTreesModel(name=external_model_binary_feature,trees="+trees+") model applied to features, sum of:\n" +
" 0.0 = tree 0 | \\'user_device_smartphone\\':0.0 <= 0.500001, Go Left | val: 0.0\n" +
" 65.0 = tree 1 | \\'user_device_tablet\\':1.0 > 0.500001, Go Right | val: 65.0\n'}");
assertJQ(
"/query" + query.toQueryString(),
"/debug/explain/9=='\n" +
"65.0 = MultipleAdditiveTreesModel(name=external_model_binary_feature,trees="+trees+") model applied to features, sum of:\n" +
" 0.0 = tree 0 | \\'user_device_smartphone\\':0.0 <= 0.500001, Go Left | val: 0.0\n" +
" 65.0 = tree 1 | \\'user_device_tablet\\':1.0 > 0.500001, Go Right | val: 65.0\n'}");
}
}