From bfc3690d5203cee20550450bac3771e5c2b85cbf Mon Sep 17 00:00:00 2001 From: Christine Poerschke Date: Wed, 7 Dec 2016 20:43:49 +0000 Subject: [PATCH] SOLR-8542: couple of tweaks (Michael Nilsson, Diego Ceccarelli, Christine Poerschke) * removed code triplication in ManagedModelStore * LTRScoringQuery.java tweaks * FeatureLogger.makeFeatureVector(...) can now safely be called repeatedly (though that doesn't happen at present) * make Feature.FeatureWeight.extractTerms a no-op; (OriginalScore|SolrFeature)Weight now implement extractTerms * LTRThreadModule javadocs and README.md tweaks * add TestFieldValueFeature.testBooleanValue test; replace "T"/"F" magic string use in FieldValueFeature * add TestOriginalScoreScorer test; add OriginalScoreScorer.freq() method * in TestMultipleAdditiveTreesModel revive dead explain test --- solr/contrib/ltr/README.md | 6 +-- .../org/apache/solr/ltr/FeatureLogger.java | 10 ++-- .../org/apache/solr/ltr/LTRScoringQuery.java | 22 ++++----- .../org/apache/solr/ltr/LTRThreadModule.java | 29 +++++++++++ .../org/apache/solr/ltr/feature/Feature.java | 3 +- .../solr/ltr/feature/FieldValueFeature.java | 18 ++++--- .../ltr/feature/OriginalScoreFeature.java | 12 ++++- .../apache/solr/ltr/feature/SolrFeature.java | 17 +++++-- .../ltr/store/rest/ManagedFeatureStore.java | 1 - .../ltr/store/rest/ManagedModelStore.java | 32 +++++-------- .../solr/collection1/conf/schema.xml | 2 + .../ltr/feature/TestFieldValueFeature.java | 48 ++++++++++++++++--- .../ltr/feature/TestOriginalScoreScorer.java | 47 ++++++++++++++++++ .../model/TestMultipleAdditiveTreesModel.java | 44 ++++++++--------- .../org/apache/solr/schema/BoolField.java | 4 +- 15 files changed, 212 insertions(+), 83 deletions(-) create mode 100644 solr/contrib/ltr/src/test/org/apache/solr/ltr/feature/TestOriginalScoreScorer.java diff --git a/solr/contrib/ltr/README.md b/solr/contrib/ltr/README.md index 88e2f67b941..2033ffc3e7b 100644 --- a/solr/contrib/ltr/README.md +++ b/solr/contrib/ltr/README.md @@ -390,17 +390,17 @@ About half the time for ranking is spent in the creation of weights for each fea 10 - 5 + 5 10 - 5 + 5 ``` -The threadModule.totalPoolThreads option limits the total number of threads to be used across all query instances at any given time. threadModule.numThreadsPerRequest limits the number of threads used to process a single query. In the above example, 10 threads will be used to services all queries and a maximum of 5 threads to service a single query. If the solr instances is expected to receive no more than one query at a time, it is best to set both these numbers to the same value. If multiple queries need to serviced simultaneously, the numbers can be adjusted based on the expected response times. If the value of threadModule.numThreadsPerRequest is higher, the reponse time for a single query will be improved upto a point. If multiple queries are serviced simultaneously, the threadModule.totalPoolThreads imposes a contention between the queries if (threadModule.numThreadsPerRequest*total parallel queries > threadModule.totalPoolThreads). +The threadModule.totalPoolThreads option limits the total number of threads to be used across all query instances at any given time. threadModule.numThreadsPerRequest limits the number of threads used to process a single query. In the above example, 10 threads will be used to services all queries and a maximum of 5 threads to service a single query. If the solr instance is expected to receive no more than one query at a time, it is best to set both these numbers to the same value. If multiple queries need to be serviced simultaneously, the numbers can be adjusted based on the expected response times. If the value of threadModule.numThreadsPerRequest is higher, the response time for a single query will be improved upto a point. If multiple queries are serviced simultaneously, the threadModule.totalPoolThreads imposes a contention between the queries if (threadModule.numThreadsPerRequest*total parallel queries > threadModule.totalPoolThreads). diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/FeatureLogger.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/FeatureLogger.java index a5afd05952c..9c10c2c6917 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/FeatureLogger.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/FeatureLogger.java @@ -151,7 +151,6 @@ public abstract class FeatureLogger { } public static class CSVFeatureLogger extends FeatureLogger { - StringBuilder sb = new StringBuilder(500); char keyValueSep = ':'; char featureSep = ';'; @@ -171,6 +170,10 @@ public abstract class FeatureLogger { @Override public String makeFeatureVector(LTRScoringQuery.FeatureInfo[] featuresInfo) { + // Allocate the buffer to a size based on the number of features instead of the + // default 16. You need space for the name, value, and two separators per feature, + // but not all the features are expected to fire, so this is just a naive estimate. + StringBuilder sb = new StringBuilder(featuresInfo.length * 3); boolean isDense = featureFormat.equals(FeatureFormat.DENSE); for (LTRScoringQuery.FeatureInfo featInfo:featuresInfo) { if (featInfo.isUsed() || isDense){ @@ -181,9 +184,8 @@ public abstract class FeatureLogger { } } - final String features = (sb.length() > 0 ? sb.substring(0, - sb.length() - 1) : ""); - sb.setLength(0); + final String features = (sb.length() > 0 ? + sb.substring(0, sb.length() - 1) : ""); return features; } diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRScoringQuery.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRScoringQuery.java index 991c1edf58f..d60ebf55bb0 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRScoringQuery.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRScoringQuery.java @@ -205,10 +205,10 @@ public class LTRScoringQuery extends Query { List featureWeights = new ArrayList<>(features.size()); if (querySemaphore == null) { - createWeights(searcher, needsScores, boost, featureWeights, features); + createWeights(searcher, needsScores, featureWeights, features); } else{ - createWeightsParallel(searcher, needsScores, boost, featureWeights, features); + createWeightsParallel(searcher, needsScores, featureWeights, features); } int i=0, j = 0; if (this.extractAllFeatures) { @@ -228,7 +228,7 @@ public class LTRScoringQuery extends Query { return new ModelWeight(modelFeaturesWeights, extractedFeatureWeights, allFeatures.size()); } - private void createWeights(IndexSearcher searcher, boolean needsScores, float boost, + private void createWeights(IndexSearcher searcher, boolean needsScores, List featureWeights, Collection features) throws IOException { final SolrQueryRequest req = getRequest(); // since the feature store is a linkedhashmap order is preserved @@ -271,7 +271,7 @@ public class LTRScoringQuery extends Query { } } // end of call CreateWeightCallable - private void createWeightsParallel(IndexSearcher searcher, boolean needsScores, float boost, + private void createWeightsParallel(IndexSearcher searcher, boolean needsScores, List featureWeights, Collection features) throws RuntimeException { final SolrQueryRequest req = getRequest(); @@ -401,8 +401,9 @@ public class LTRScoringQuery extends Query { /** * Goes through all the stored feature values, and calculates the normalized * values for all the features that will be used for scoring. + * Then calculate and return the model's score. */ - private void makeNormalizedFeatures() { + private float makeNormalizedFeaturesAndScore() { int pos = 0; for (final Feature.FeatureWeight feature : modelFeatureWeights) { final int featureId = feature.getIndex(); @@ -415,6 +416,7 @@ public class LTRScoringQuery extends Query { pos++; } ltrScoringModel.normalizeFeaturesInPlace(modelFeatureValuesNormalized); + return ltrScoringModel.score(modelFeatureValuesNormalized); } @Override @@ -491,8 +493,8 @@ public class LTRScoringQuery extends Query { for (final Feature.FeatureWeight.FeatureScorer subSocer : featureScorers) { subSocer.setDocInfo(docInfo); } - if (featureScorers.size() <= 1) { // TODO: Allow the use of dense - // features in other cases + if (featureScorers.size() <= 1) { + // future enhancement: allow the use of dense features in other cases featureTraversalScorer = new DenseModelScorer(weight, featureScorers); } else { featureTraversalScorer = new SparseModelScorer(weight, featureScorers); @@ -570,8 +572,7 @@ public class LTRScoringQuery extends Query { featuresInfo[featureId].setUsed(true); } } - makeNormalizedFeatures(); - return ltrScoringModel.score(modelFeatureValuesNormalized); + return makeNormalizedFeaturesAndScore(); } @Override @@ -663,8 +664,7 @@ public class LTRScoringQuery extends Query { } } } - makeNormalizedFeatures(); - return ltrScoringModel.score(modelFeatureValuesNormalized); + return makeNormalizedFeaturesAndScore(); } @Override diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRThreadModule.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRThreadModule.java index 8e2563f1e08..b8d0bda3a46 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRThreadModule.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRThreadModule.java @@ -29,6 +29,35 @@ import org.apache.solr.util.DefaultSolrThreadFactory; import org.apache.solr.util.SolrPluginUtils; import org.apache.solr.util.plugin.NamedListInitializedPlugin; +/** + * The LTRThreadModule is optionally used by the {@link org.apache.solr.ltr.search.LTRQParserPlugin} and + * {@link org.apache.solr.ltr.response.transform.LTRFeatureLoggerTransformerFactory LTRFeatureLoggerTransformerFactory} + * classes to parallelize the creation of {@link org.apache.solr.ltr.feature.Feature.FeatureWeight Feature.FeatureWeight} + * objects. + *

+ * Example configuration: + *

+  <queryParser name="ltr" class="org.apache.solr.ltr.search.LTRQParserPlugin">
+     <int name="threadModule.totalPoolThreads">10</int>
+     <int name="threadModule.numThreadsPerRequest">5</int>
+  </queryParser>
+
+  <transformer name="features" class="org.apache.solr.ltr.response.transform.LTRFeatureLoggerTransformerFactory">
+     <int name="threadModule.totalPoolThreads">10</int>
+     <int name="threadModule.numThreadsPerRequest">5</int>
+  </transformer>
+
+ * If an individual solr instance is expected to receive no more than one query at a time, it is best + * to set totalPoolThreads and numThreadsPerRequest to the same value. + * + * If multiple queries need to be serviced simultaneously then totalPoolThreads and + * numThreadsPerRequest can be adjusted based on the expected response times. + * + * If the value of numThreadsPerRequest is higher, the response time for a single query + * will be improved up to a point. If multiple queries are serviced simultaneously, the value of + * totalPoolThreads imposes a contention between the queries if + * (totalPoolThreads < numThreadsPerRequest * total parallel queries). + */ final public class LTRThreadModule implements NamedListInitializedPlugin { public static LTRThreadModule getInstance(NamedList args) { diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/Feature.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/Feature.java index 228b964e6b9..48e89423ca1 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/Feature.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/Feature.java @@ -258,8 +258,7 @@ public abstract class Feature extends Query { @Override public void extractTerms(Set terms) { - // needs to be implemented by query subclasses - throw new UnsupportedOperationException(); + // no-op } /** diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/FieldValueFeature.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/FieldValueFeature.java index 279adbc3ca3..5fcf144d89c 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/FieldValueFeature.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/FieldValueFeature.java @@ -29,6 +29,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.schema.BoolField; /** * This feature returns the value of a field in the current document @@ -119,13 +120,16 @@ public class FieldValueFeature extends Feature { return number.floatValue(); } else { final String string = indexableField.stringValue(); - // boolean values in the index are encoded with the - // chars T/F - if (string.equals("T")) { - return 1; - } - if (string.equals("F")) { - return 0; + if (string.length() == 1) { + // boolean values in the index are encoded with the + // a single char contained in TRUE_TOKEN or FALSE_TOKEN + // (see BoolField) + if (string.charAt(0) == BoolField.TRUE_TOKEN[0]) { + return 1; + } + if (string.charAt(0) == BoolField.FALSE_TOKEN[0]) { + return 0; + } } } } catch (final IOException e) { diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/OriginalScoreFeature.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/OriginalScoreFeature.java index 125615cbb4f..549880be06e 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/OriginalScoreFeature.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/OriginalScoreFeature.java @@ -19,8 +19,10 @@ package org.apache.solr.ltr.feature; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; @@ -76,7 +78,10 @@ public class OriginalScoreFeature extends Feature { return "OriginalScoreFeature [query:" + originalQuery.toString() + "]"; } - + @Override + public void extractTerms(Set terms) { + w.extractTerms(terms); + } @Override public FeatureScorer scorer(LeafReaderContext context) throws IOException { @@ -102,6 +107,11 @@ public class OriginalScoreFeature extends Feature { return (docInfo.hasOriginalDocScore() ? docInfo.getOriginalDocScore() : originalScorer.score()); } + @Override + public int freq() throws IOException { + return originalScorer.freq(); + } + @Override public int docID() { return originalScorer.docID(); diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/SolrFeature.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/SolrFeature.java index cb7c1a0c81a..13eb96fee2a 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/SolrFeature.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/SolrFeature.java @@ -21,8 +21,10 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; @@ -123,9 +125,9 @@ public class SolrFeature extends Feature { * Weight for a SolrFeature **/ public class SolrFeatureWeight extends FeatureWeight { - Weight solrQueryWeight; - Query query; - List queryAndFilters; + final private Weight solrQueryWeight; + final private Query query; + final private List queryAndFilters; public SolrFeatureWeight(IndexSearcher searcher, SolrQueryRequest request, Query originalQuery, Map efi) throws IOException { @@ -174,6 +176,8 @@ public class SolrFeature extends Feature { if (query != null) { queryAndFilters.add(query); solrQueryWeight = searcher.createNormalizedWeight(query, true); + } else { + solrQueryWeight = null; } } catch (final SyntaxError e) { throw new FeatureException("Failed to parse feature query.", e); @@ -201,6 +205,13 @@ public class SolrFeature extends Feature { } } + @Override + public void extractTerms(Set terms) { + if (solrQueryWeight != null) { + solrQueryWeight.extractTerms(terms); + } + } + @Override public FeatureScorer scorer(LeafReaderContext context) throws IOException { Scorer solrScorer = null; diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/store/rest/ManagedFeatureStore.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/store/rest/ManagedFeatureStore.java index beb217c5c37..2c7bce58156 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/store/rest/ManagedFeatureStore.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/store/rest/ManagedFeatureStore.java @@ -57,7 +57,6 @@ public class ManagedFeatureStore extends ManagedResource implements ManagedResou /** the feature store rest endpoint **/ public static final String REST_END_POINT = "/schema/feature-store"; - // TODO: reduce from public to package visibility (once tests no longer need public access) /** name of the attribute containing the feature class **/ static final String CLASS_KEY = "class"; diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/store/rest/ManagedModelStore.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/store/rest/ManagedModelStore.java index 97aaa4004ad..9c19b0a7c26 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/store/rest/ManagedModelStore.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/store/rest/ManagedModelStore.java @@ -61,7 +61,6 @@ public class ManagedModelStore extends ManagedResource implements ManagedResourc /** the model store rest endpoint **/ public static final String REST_END_POINT = "/schema/model-store"; - // TODO: reduce from public to package visibility (once tests no longer need public access) /** * Managed model store: the name of the attribute containing all the models of @@ -124,16 +123,20 @@ public class ManagedModelStore extends ManagedResource implements ManagedResourc if ((managedData != null) && (managedData instanceof List)) { final List> up = (List>) managedData; for (final Map u : up) { - try { - final LTRScoringModel algo = fromLTRScoringModelMap(solrResourceLoader, u, managedFeatureStore); - addModel(algo); - } catch (final ModelException e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); - } + addModelFromMap(u); } } } + private void addModelFromMap(Map modelMap) { + try { + final LTRScoringModel algo = fromLTRScoringModelMap(solrResourceLoader, modelMap, managedFeatureStore); + addModel(algo); + } catch (final ModelException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); + } + } + public synchronized void addModel(LTRScoringModel ltrScoringModel) throws ModelException { try { log.info("adding model {}", ltrScoringModel.getName()); @@ -146,26 +149,17 @@ public class ManagedModelStore extends ManagedResource implements ManagedResourc @SuppressWarnings("unchecked") @Override protected Object applyUpdatesToManagedData(Object updates) { + if (updates instanceof List) { final List> up = (List>) updates; for (final Map u : up) { - try { - final LTRScoringModel algo = fromLTRScoringModelMap(solrResourceLoader, u, managedFeatureStore); - addModel(algo); - } catch (final ModelException e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); - } + addModelFromMap(u); } } if (updates instanceof Map) { final Map map = (Map) updates; - try { - final LTRScoringModel algo = fromLTRScoringModelMap(solrResourceLoader, map, managedFeatureStore); - addModel(algo); - } catch (final ModelException e) { - throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); - } + addModelFromMap(map); } return modelsAsManagedResources(store.getModels()); diff --git a/solr/contrib/ltr/src/test-files/solr/collection1/conf/schema.xml b/solr/contrib/ltr/src/test-files/solr/collection1/conf/schema.xml index 15cf140cc09..0b958c0aca3 100644 --- a/solr/contrib/ltr/src/test-files/solr/collection1/conf/schema.xml +++ b/solr/contrib/ltr/src/test-files/solr/collection1/conf/schema.xml @@ -24,6 +24,8 @@ + + diff --git a/solr/contrib/ltr/src/test/org/apache/solr/ltr/feature/TestFieldValueFeature.java b/solr/contrib/ltr/src/test/org/apache/solr/ltr/feature/TestFieldValueFeature.java index af150c060e4..95742733cc7 100644 --- a/solr/contrib/ltr/src/test/org/apache/solr/ltr/feature/TestFieldValueFeature.java +++ b/solr/contrib/ltr/src/test/org/apache/solr/ltr/feature/TestFieldValueFeature.java @@ -32,21 +32,21 @@ public class TestFieldValueFeature extends TestRerankBase { setuptest("solrconfig-ltr.xml", "schema.xml"); assertU(adoc("id", "1", "title", "w1", "description", "w1", "popularity", - "1")); + "1","isTrendy","true")); assertU(adoc("id", "2", "title", "w2 2asd asdd didid", "description", "w2 2asd asdd didid", "popularity", "2")); assertU(adoc("id", "3", "title", "w3", "description", "w3", "popularity", - "3")); + "3","isTrendy","true")); assertU(adoc("id", "4", "title", "w4", "description", "w4", "popularity", - "4")); + "4","isTrendy","false")); assertU(adoc("id", "5", "title", "w5", "description", "w5", "popularity", - "5")); + "5","isTrendy","true")); assertU(adoc("id", "6", "title", "w1 w2", "description", "w1 w2", - "popularity", "6")); + "popularity", "6","isTrendy","false")); assertU(adoc("id", "7", "title", "w1 w2 w3 w4 w5", "description", - "w1 w2 w3 w4 w5 w8", "popularity", "7")); + "w1 w2 w3 w4 w5 w8", "popularity", "7","isTrendy","true")); assertU(adoc("id", "8", "title", "w1 w1 w1 w2 w2 w8", "description", - "w1 w1 w1 w2 w2", "popularity", "8")); + "w1 w1 w1 w2 w2", "popularity", "8","isTrendy","false")); // a document without the popularity field assertU(adoc("id", "42", "title", "NO popularity", "description", "NO popularity")); @@ -169,5 +169,39 @@ public class TestFieldValueFeature extends TestRerankBase { } + @Test + public void testBooleanValue() throws Exception { + final String fstore = "test_boolean_store"; + loadFeature("trendy", FieldValueFeature.class.getCanonicalName(), fstore, + "{\"field\":\"isTrendy\"}"); + + loadModel("trendy-model", LinearModel.class.getCanonicalName(), + new String[] {"trendy"}, fstore, "{\"weights\":{\"trendy\":1.0}}"); + + SolrQuery query = new SolrQuery(); + query.setQuery("id:4"); + query.add("rq", "{!ltr model=trendy-model reRankDocs=4}"); + query.add("fl", "[fv]"); + assertJQ("/query" + query.toQueryString(), + "/response/docs/[0]/=={'[fv]':'trendy:0.0'}"); + + + query = new SolrQuery(); + query.setQuery("id:5"); + query.add("rq", "{!ltr model=trendy-model reRankDocs=4}"); + query.add("fl", "[fv]"); + assertJQ("/query" + query.toQueryString(), + "/response/docs/[0]/=={'[fv]':'trendy:1.0'}"); + + // check default value is false + query = new SolrQuery(); + query.setQuery("id:2"); + query.add("rq", "{!ltr model=trendy-model reRankDocs=4}"); + query.add("fl", "[fv]"); + assertJQ("/query" + query.toQueryString(), + "/response/docs/[0]/=={'[fv]':'trendy:0.0'}"); + + } + } diff --git a/solr/contrib/ltr/src/test/org/apache/solr/ltr/feature/TestOriginalScoreScorer.java b/solr/contrib/ltr/src/test/org/apache/solr/ltr/feature/TestOriginalScoreScorer.java new file mode 100644 index 00000000000..e85ebedf084 --- /dev/null +++ b/solr/contrib/ltr/src/test/org/apache/solr/ltr/feature/TestOriginalScoreScorer.java @@ -0,0 +1,47 @@ +/* + * 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.feature; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.apache.lucene.search.Scorer; +import org.apache.lucene.util.LuceneTestCase; +import org.junit.Test; + +public class TestOriginalScoreScorer extends LuceneTestCase { + + @Test + public void testOverridesAbstractScorerMethods() { + final Class ossClass = OriginalScoreFeature.OriginalScoreWeight.OriginalScoreScorer.class; + for (final Method scorerClassMethod : Scorer.class.getDeclaredMethods()) { + final int modifiers = scorerClassMethod.getModifiers(); + if (!Modifier.isAbstract(modifiers)) continue; + + try { + final Method ossClassMethod = ossClass.getDeclaredMethod( + scorerClassMethod.getName(), + scorerClassMethod.getParameterTypes()); + assertEquals("getReturnType() difference", + scorerClassMethod.getReturnType(), + ossClassMethod.getReturnType()); + } catch (NoSuchMethodException e) { + fail(ossClass + " needs to override '" + scorerClassMethod + "'"); + } + } + } +} diff --git a/solr/contrib/ltr/src/test/org/apache/solr/ltr/model/TestMultipleAdditiveTreesModel.java b/solr/contrib/ltr/src/test/org/apache/solr/ltr/model/TestMultipleAdditiveTreesModel.java index 3748331a43e..560437078cb 100644 --- a/solr/contrib/ltr/src/test/org/apache/solr/ltr/model/TestMultipleAdditiveTreesModel.java +++ b/solr/contrib/ltr/src/test/org/apache/solr/ltr/model/TestMultipleAdditiveTreesModel.java @@ -16,7 +16,7 @@ */ package org.apache.solr.ltr.model; -//import static org.junit.internal.matchers.StringContains.containsString; +import static org.junit.internal.matchers.StringContains.containsString; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.ltr.TestRerankBase; @@ -93,30 +93,28 @@ public class TestMultipleAdditiveTreesModel extends TestRerankBase { // test out the explain feature, make sure it returns something query.setParam("debugQuery", "on"); - String qryResult = JQ("/query" + query.toQueryString()); + String qryResult = JQ("/query" + query.toQueryString()); qryResult = qryResult.replaceAll("\n", " "); - // FIXME containsString doesn't exist. - // assertThat(qryResult, containsString("\"debug\":{")); - // qryResult = qryResult.substring(qryResult.indexOf("debug")); - // - // assertThat(qryResult, containsString("\"explain\":{")); - // qryResult = qryResult.substring(qryResult.indexOf("explain")); - // - // assertThat(qryResult, containsString("multipleadditivetreesmodel")); - // assertThat(qryResult, - // containsString(MultipleAdditiveTreesModel.class.getCanonicalName())); - // - // assertThat(qryResult, containsString("-100.0 = tree 0")); - // assertThat(qryResult, containsString("50.0 = tree 0")); - // assertThat(qryResult, containsString("-20.0 = tree 1")); - // assertThat(qryResult, containsString("'matchedTitle':1.0 > 0.5")); - // assertThat(qryResult, containsString("'matchedTitle':0.0 <= 0.5")); - // - // assertThat(qryResult, containsString(" Go Right ")); - // assertThat(qryResult, containsString(" Go Left ")); - // assertThat(qryResult, - // containsString("'this_feature_doesnt_exist' does not exist in FV")); + + assertThat(qryResult, containsString("\"debug\":{")); + qryResult = qryResult.substring(qryResult.indexOf("debug")); + + assertThat(qryResult, containsString("\"explain\":{")); + qryResult = qryResult.substring(qryResult.indexOf("explain")); + + assertThat(qryResult, containsString("multipleadditivetreesmodel")); + assertThat(qryResult, containsString(MultipleAdditiveTreesModel.class.getCanonicalName())); + + assertThat(qryResult, containsString("-100.0 = tree 0")); + assertThat(qryResult, containsString("50.0 = tree 0")); + assertThat(qryResult, containsString("-20.0 = tree 1")); + assertThat(qryResult, containsString("'matchedTitle':1.0 > 0.5")); + assertThat(qryResult, containsString("'matchedTitle':0.0 <= 0.5")); + + assertThat(qryResult, containsString(" Go Right ")); + assertThat(qryResult, containsString(" Go Left ")); + assertThat(qryResult, containsString("'this_feature_doesnt_exist' does not exist in FV")); } @Test diff --git a/solr/core/src/java/org/apache/solr/schema/BoolField.java b/solr/core/src/java/org/apache/solr/schema/BoolField.java index 210ea0ba103..1645ee6cbf6 100644 --- a/solr/core/src/java/org/apache/solr/schema/BoolField.java +++ b/solr/core/src/java/org/apache/solr/schema/BoolField.java @@ -71,8 +71,8 @@ public class BoolField extends PrimitiveFieldType { } // avoid instantiating every time... - protected final static char[] TRUE_TOKEN = {'T'}; - protected final static char[] FALSE_TOKEN = {'F'}; + public final static char[] TRUE_TOKEN = {'T'}; + public final static char[] FALSE_TOKEN = {'F'}; //////////////////////////////////////////////////////////////////////// // TODO: look into creating my own queryParser that can more efficiently