diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index df4a7231fb1..f341eaf76f1 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -117,6 +117,9 @@ New Features * SOLR-2396: Add CollationField, which is much more efficient than the Solr 3.x CollationKeyFilterFactory, and also supports Locale-sensitive range queries. (rmuir) + +* SOLR-2338: Add support for using in a schema's fieldType, + for customizing scoring on a per-field basis. (hossman, yonik, rmuir) Optimizations diff --git a/solr/src/common/org/apache/solr/common/util/NamedList.java b/solr/src/common/org/apache/solr/common/util/NamedList.java index 579a7f29008..10b1996e608 100644 --- a/solr/src/common/org/apache/solr/common/util/NamedList.java +++ b/solr/src/common/org/apache/solr/common/util/NamedList.java @@ -43,7 +43,7 @@ import java.io.Serializable; * by key, so ResponseWriters that output to a format such as JSON will normally * choose a data structure that allows order to be easily preserved in various * clients (i.e. not a straight map). - * If access by key is more important, see {@link SimpleOrderedMap}, + * If access by key is more important for serialization, see {@link SimpleOrderedMap}, * or simply use a regular {@link Map} *

* @@ -200,10 +200,14 @@ public class NamedList implements Cloneable, Serializable, Iterable + * NOTE: this runs in linear time (it scans starting at the + * beginning of the list until it finds the first pair with + * the specified name). * @return null if not found or if the value stored was null. * @see #indexOf * @see #get(String,int) + * */ public T get(String name) { return get(name,0); @@ -212,7 +216,10 @@ public class NamedList implements Cloneable, Serializable, Iterable + * NOTE: this runs in linear time (it scans starting at the + * specified position until it finds the first pair with + * the specified name). * @return null if not found or if the value stored was null. * @see #indexOf */ @@ -377,6 +384,11 @@ public class NamedList implements Cloneable, Serializable, Iterable + * The default implementation returns null, which means this type + * has no custom similarity associated with it. + *

+ * + * This method exists to internally support SolrSimilarityProvider. + * Custom application code interested in a field's Similarity should + * instead query via the searcher's SimilarityProvider. + * @lucene.internal + */ + public Similarity getSimilarity() { + return similarity; + } + + /** + * Sets the Similarity used when scoring fields of this type + * @lucene.internal + */ + public void setSimilarity(Similarity similarity) { + this.similarity = similarity; + } + /** * calls back to TextResponseWriter to write the field value */ diff --git a/solr/src/java/org/apache/solr/schema/IndexSchema.java b/solr/src/java/org/apache/solr/schema/IndexSchema.java index e7732e3d3d7..b6b7b6768f5 100644 --- a/solr/src/java/org/apache/solr/schema/IndexSchema.java +++ b/solr/src/java/org/apache/solr/schema/IndexSchema.java @@ -20,7 +20,9 @@ package org.apache.solr.schema; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.document.Fieldable; +import org.apache.lucene.search.DefaultSimilarity; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Similarity; import org.apache.lucene.search.SimilarityProvider; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.util.Version; @@ -28,6 +30,7 @@ import org.apache.solr.common.ResourceLoader; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.DOMUtil; +import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SystemIdResolver; import org.apache.solr.core.SolrConfig; import org.apache.solr.core.Config; @@ -37,6 +40,7 @@ import org.apache.solr.analysis.TokenFilterFactory; import org.apache.solr.analysis.TokenizerChain; import org.apache.solr.analysis.TokenizerFactory; import org.apache.solr.search.SolrQueryParser; +import org.apache.solr.search.SolrSimilarityProvider; import org.apache.solr.util.plugin.AbstractPluginLoader; import org.apache.solr.util.plugin.SolrCoreAware; import org.w3c.dom.*; @@ -185,19 +189,22 @@ public final class IndexSchema { */ public Collection getRequiredFields() { return requiredFields; } - private SimilarityFactory similarityFactory; + private SimilarityProviderFactory similarityProviderFactory; /** - * Returns the Similarity used for this index + * Returns the SimilarityProvider used for this index */ - public SimilarityProvider getSimilarityProvider() { return similarityFactory.getSimilarityProvider(); } + public SimilarityProvider getSimilarityProvider() { return similarityProviderFactory.getSimilarityProvider(this); } /** - * Returns the SimilarityFactory used for this index + * Returns the SimilarityProviderFactory used for this index */ - public SimilarityFactory getSimilarityFactory() { return similarityFactory; } - + public SimilarityProviderFactory getSimilarityProviderFactory() { return similarityProviderFactory; } + private Similarity fallbackSimilarity; + + /** fallback similarity, in the case a field doesnt specify */ + public Similarity getFallbackSimilarity() { return fallbackSimilarity; } /** * Returns the Analyzer used when indexing documents for this index @@ -387,6 +394,11 @@ public final class IndexSchema { expression = "./analyzer[not(@type)] | ./analyzer[@type='index']"; anode = (Node)xpath.evaluate(expression, node, XPathConstants.NODE); Analyzer analyzer = readAnalyzer(anode); + + // a custom similarity[Factory] + expression = "./similarity"; + anode = (Node)xpath.evaluate(expression, node, XPathConstants.NODE); + Similarity similarity = readSimilarity(anode); if (queryAnalyzer==null) queryAnalyzer=analyzer; if (analyzer==null) analyzer=queryAnalyzer; @@ -394,6 +406,9 @@ public final class IndexSchema { ft.setAnalyzer(analyzer); ft.setQueryAnalyzer(queryAnalyzer); } + if (similarity!=null) { + ft.setSimilarity(similarity); + } if (ft instanceof SchemaAware){ schemaAware.add((SchemaAware) ft); } @@ -491,36 +506,31 @@ public final class IndexSchema { // stuff it in a normal array for faster access dynamicFields = dFields.toArray(new DynamicField[dFields.size()]); - Node node = (Node) xpath.evaluate("/schema/similarity", document, XPathConstants.NODE); + Similarity similarity = readSimilarity(node); + fallbackSimilarity = similarity == null ? new DefaultSimilarity() : similarity; + + node = (Node) xpath.evaluate("/schema/similarityProvider", document, XPathConstants.NODE); if (node==null) { - similarityFactory = new SimilarityFactory() { + final SolrSimilarityProvider provider = new SolrSimilarityProvider(this); + similarityProviderFactory = new SimilarityProviderFactory() { @Override - public SimilarityProvider getSimilarityProvider() { - return IndexSearcher.getDefaultSimilarityProvider(); + public SolrSimilarityProvider getSimilarityProvider(IndexSchema schema) { + return provider; } }; - log.debug("using default similarity"); + log.debug("using default similarityProvider"); } else { final Object obj = loader.newInstance(((Element) node).getAttribute("class")); - if (obj instanceof SimilarityFactory) { - // configure a factory, get a similarity back - SolrParams params = SolrParams.toSolrParams(DOMUtil.childNodesToNamedList(node)); - similarityFactory = (SimilarityFactory)obj; - similarityFactory.init(params); - } else { - // just like always, assume it's a SimilarityProvider and get a ClassCastException - reasonable error handling - similarityFactory = new SimilarityFactory() { - @Override - public SimilarityProvider getSimilarityProvider() { - return (SimilarityProvider) obj; - } - }; + // just like always, assume it's a SimilarityProviderFactory and get a ClassCastException - reasonable error handling + // configure a factory, get a similarity back + NamedList args = DOMUtil.childNodesToNamedList(node); + similarityProviderFactory = (SimilarityProviderFactory)obj; + similarityProviderFactory.init(args); + if (similarityProviderFactory instanceof SchemaAware){ + schemaAware.add((SchemaAware) similarityProviderFactory); } - if (similarityFactory instanceof SchemaAware){ - schemaAware.add((SchemaAware) similarityFactory); - } - log.debug("using similarity factory" + similarityFactory.getClass().getName()); + log.debug("using similarityProvider factory" + similarityProviderFactory.getClass().getName()); } node = (Node) xpath.evaluate("/schema/defaultSearchField/text()", document, XPathConstants.NODE); @@ -750,6 +760,30 @@ public final class IndexSchema { return newArr; } + private Similarity readSimilarity(Node node) throws XPathExpressionException { + if (node==null) { + return null; + } else { + SimilarityFactory similarityFactory; + final Object obj = loader.newInstance(((Element) node).getAttribute("class")); + if (obj instanceof SimilarityFactory) { + // configure a factory, get a similarity back + SolrParams params = SolrParams.toSolrParams(DOMUtil.childNodesToNamedList(node)); + similarityFactory = (SimilarityFactory)obj; + similarityFactory.init(params); + } else { + // just like always, assume it's a Similarity and get a ClassCastException - reasonable error handling + similarityFactory = new SimilarityFactory() { + @Override + public Similarity getSimilarity() { + return (Similarity) obj; + } + }; + } + return similarityFactory.getSimilarity(); + } + } + // // // diff --git a/solr/src/java/org/apache/solr/schema/SimilarityFactory.java b/solr/src/java/org/apache/solr/schema/SimilarityFactory.java index f2e29d575b5..25b03a0ad68 100644 --- a/solr/src/java/org/apache/solr/schema/SimilarityFactory.java +++ b/solr/src/java/org/apache/solr/schema/SimilarityFactory.java @@ -16,7 +16,7 @@ package org.apache.solr.schema; * limitations under the License. */ -import org.apache.lucene.search.SimilarityProvider; +import org.apache.lucene.search.Similarity; import org.apache.solr.common.params.SolrParams; public abstract class SimilarityFactory { @@ -25,5 +25,5 @@ public abstract class SimilarityFactory { public void init(SolrParams params) { this.params = params; } public SolrParams getParams() { return params; } - public abstract SimilarityProvider getSimilarityProvider(); + public abstract Similarity getSimilarity(); } diff --git a/solr/src/java/org/apache/solr/schema/SimilarityProviderFactory.java b/solr/src/java/org/apache/solr/schema/SimilarityProviderFactory.java new file mode 100644 index 00000000000..dafb9411df2 --- /dev/null +++ b/solr/src/java/org/apache/solr/schema/SimilarityProviderFactory.java @@ -0,0 +1,34 @@ +package org.apache.solr.schema; + +/** + * 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. + */ + +import org.apache.solr.common.util.NamedList; +import org.apache.solr.search.SolrSimilarityProvider; + +/** + * Expert: Factory to provide a {@link SolrSimilarityProvider}. + *

+ * Usually you would implement this if you want to customize the + * scoring routines that are not field-specific, such as coord() and queryNorm(). + * Most scoring customization happens in the fieldtype's Similarity + */ +public abstract class SimilarityProviderFactory { + public void init(NamedList args) {} + + public abstract SolrSimilarityProvider getSimilarityProvider(IndexSchema schema); +} diff --git a/solr/src/java/org/apache/solr/search/SolrSimilarityProvider.java b/solr/src/java/org/apache/solr/search/SolrSimilarityProvider.java new file mode 100644 index 00000000000..2fe437fd999 --- /dev/null +++ b/solr/src/java/org/apache/solr/search/SolrSimilarityProvider.java @@ -0,0 +1,56 @@ +package org.apache.solr.search; + +/** + * 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. + */ + +import org.apache.lucene.search.DefaultSimilarityProvider; +import org.apache.lucene.search.Similarity; +import org.apache.solr.schema.FieldType; +import org.apache.solr.schema.IndexSchema; + +/** + * SimilarityProvider that uses the default Lucene implementation, unless + * otherwise specified by the fieldtype. + *

+ * You can extend this class to customize the behavior of the parts + * of lucene's ranking system that are not field-specific, such as + * {@link #coord(int, int)} and {@link #queryNorm(float)}. + */ +public class SolrSimilarityProvider extends DefaultSimilarityProvider { + private final IndexSchema schema; + + public SolrSimilarityProvider(IndexSchema schema) { + this.schema = schema; + } + + /** + * Solr implementation delegates to the fieldtype's similarity. + * If this does not exist, uses the schema's default similarity. + */ + // note: this is intentionally final, to maintain consistency with + // whatever is specified in the the schema! + @Override + public final Similarity get(String field) { + FieldType fieldType = schema.getFieldTypeNoEx(field); + if (fieldType == null) { + return schema.getFallbackSimilarity(); + } else { + Similarity similarity = fieldType.getSimilarity(); + return similarity == null ? schema.getFallbackSimilarity() : similarity; + } + } +} diff --git a/solr/src/test-files/solr/conf/schema.xml b/solr/src/test-files/solr/conf/schema.xml index 6dabdde653b..f0fa272ff2b 100644 --- a/solr/src/test-files/solr/conf/schema.xml +++ b/solr/src/test-files/solr/conf/schema.xml @@ -391,6 +391,32 @@ + + + + + + + + + + + + + + + + + is there an echo? + + + + + + + + + @@ -496,6 +522,9 @@ + + + @@ -574,6 +603,10 @@ + + + + text @@ -607,12 +640,18 @@ - - + is there an echo? - + + + + I am your default sim + diff --git a/solr/src/test/org/apache/solr/schema/CustomSimilarityFactory.java b/solr/src/test/org/apache/solr/schema/CustomSimilarityFactory.java index 03fbaecaec2..98267cce285 100644 --- a/solr/src/test/org/apache/solr/schema/CustomSimilarityFactory.java +++ b/solr/src/test/org/apache/solr/schema/CustomSimilarityFactory.java @@ -16,11 +16,11 @@ */ package org.apache.solr.schema; -import org.apache.lucene.search.SimilarityProvider; +import org.apache.lucene.search.Similarity; public class CustomSimilarityFactory extends SimilarityFactory { @Override - public SimilarityProvider getSimilarityProvider() { + public Similarity getSimilarity() { return new MockConfigurableSimilarity(params.get("echo")); } } diff --git a/solr/src/test/org/apache/solr/schema/CustomSimilarityProviderFactory.java b/solr/src/test/org/apache/solr/schema/CustomSimilarityProviderFactory.java new file mode 100644 index 00000000000..68de144e6b7 --- /dev/null +++ b/solr/src/test/org/apache/solr/schema/CustomSimilarityProviderFactory.java @@ -0,0 +1,34 @@ +/** + * 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.schema; + +import org.apache.solr.common.util.NamedList; +import org.apache.solr.search.SolrSimilarityProvider; + +public class CustomSimilarityProviderFactory extends SimilarityProviderFactory { + String echoParam; + + @Override + public void init(NamedList args) { + echoParam = (String) args.get("echo"); + } + + @Override + public SolrSimilarityProvider getSimilarityProvider(IndexSchema schema) { + return new MockConfigurableSimilarityProvider(schema, echoParam); + } +} diff --git a/solr/src/test/org/apache/solr/schema/IndexSchemaTest.java b/solr/src/test/org/apache/solr/schema/IndexSchemaTest.java index cb176d8dee8..737cb90ffc1 100644 --- a/solr/src/test/org/apache/solr/schema/IndexSchemaTest.java +++ b/solr/src/test/org/apache/solr/schema/IndexSchemaTest.java @@ -81,11 +81,11 @@ public class IndexSchemaTest extends SolrTestCaseJ4 { } @Test - public void testSimilarityFactory() { + public void testSimilarityProviderFactory() { SolrCore core = h.getCore(); - SimilarityProvider similarity = core.getSchema().getSimilarityProvider(); - assertTrue("wrong class", similarity instanceof MockConfigurableSimilarity); - assertEquals("is there an echo?", ((MockConfigurableSimilarity)similarity).getPassthrough()); + SimilarityProvider similarityProvider = core.getSchema().getSimilarityProvider(); + assertTrue("wrong class", similarityProvider instanceof MockConfigurableSimilarityProvider); + assertEquals("is there an echo?", ((MockConfigurableSimilarityProvider)similarityProvider).getPassthrough()); } @Test diff --git a/solr/src/test/org/apache/solr/schema/MockConfigurableSimilarity.java b/solr/src/test/org/apache/solr/schema/MockConfigurableSimilarity.java index 6b8f6c1d362..36021fab4e7 100644 --- a/solr/src/test/org/apache/solr/schema/MockConfigurableSimilarity.java +++ b/solr/src/test/org/apache/solr/schema/MockConfigurableSimilarity.java @@ -16,9 +16,9 @@ */ package org.apache.solr.schema; -import org.apache.lucene.search.DefaultSimilarityProvider; +import org.apache.lucene.search.DefaultSimilarity; -public class MockConfigurableSimilarity extends DefaultSimilarityProvider { +public class MockConfigurableSimilarity extends DefaultSimilarity { private String passthrough; public MockConfigurableSimilarity(String passthrough) { diff --git a/solr/src/test/org/apache/solr/schema/MockConfigurableSimilarityProvider.java b/solr/src/test/org/apache/solr/schema/MockConfigurableSimilarityProvider.java new file mode 100644 index 00000000000..3a6e8a40ed0 --- /dev/null +++ b/solr/src/test/org/apache/solr/schema/MockConfigurableSimilarityProvider.java @@ -0,0 +1,32 @@ +/** + * 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.schema; + +import org.apache.solr.search.SolrSimilarityProvider; + +public class MockConfigurableSimilarityProvider extends SolrSimilarityProvider { + private String passthrough; + + public MockConfigurableSimilarityProvider(IndexSchema schema, String passthrough) { + super(schema); + this.passthrough = passthrough; + } + + public String getPassthrough() { + return passthrough; + } +} diff --git a/solr/src/test/org/apache/solr/schema/TestPerFieldSimilarity.java b/solr/src/test/org/apache/solr/schema/TestPerFieldSimilarity.java new file mode 100644 index 00000000000..3aa81c30c84 --- /dev/null +++ b/solr/src/test/org/apache/solr/schema/TestPerFieldSimilarity.java @@ -0,0 +1,106 @@ +package org.apache.solr.schema; + +/** + * 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. + */ + +import org.apache.lucene.misc.SweetSpotSimilarity; +import org.apache.lucene.search.DefaultSimilarity; +import org.apache.lucene.search.Similarity; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.core.SolrCore; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.util.RefCounted; +import org.junit.BeforeClass; + +/** + * Tests per-field similarity support in the schema + */ +public class TestPerFieldSimilarity extends SolrTestCaseJ4 { + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig.xml","schema.xml"); + } + + /** test a field where the sim is specified directly */ + public void testDirect() throws Exception { + SolrCore core = h.getCore(); + RefCounted searcher = core.getSearcher(); + Similarity sim = searcher.get().getSimilarityProvider().get("sim1text"); + assertEquals(SweetSpotSimilarity.class, sim.getClass()); + searcher.decref(); + } + + /** ... and for a dynamic field */ + public void testDirectDynamic() throws Exception { + SolrCore core = h.getCore(); + RefCounted searcher = core.getSearcher(); + Similarity sim = searcher.get().getSimilarityProvider().get("text_sim1"); + assertEquals(SweetSpotSimilarity.class, sim.getClass()); + searcher.decref(); + } + + /** test a field where a configurable sim factory is defined */ + public void testFactory() throws Exception { + SolrCore core = h.getCore(); + RefCounted searcher = core.getSearcher(); + Similarity sim = searcher.get().getSimilarityProvider().get("sim2text"); + assertEquals(MockConfigurableSimilarity.class, sim.getClass()); + assertEquals("is there an echo?", ((MockConfigurableSimilarity)sim).getPassthrough()); + searcher.decref(); + } + + /** ... and for a dynamic field */ + public void testFactoryDynamic() throws Exception { + SolrCore core = h.getCore(); + RefCounted searcher = core.getSearcher(); + Similarity sim = searcher.get().getSimilarityProvider().get("text_sim2"); + assertEquals(MockConfigurableSimilarity.class, sim.getClass()); + assertEquals("is there an echo?", ((MockConfigurableSimilarity)sim).getPassthrough()); + searcher.decref(); + } + + /** test a field where no similarity is specified */ + public void testDefaults() throws Exception { + SolrCore core = h.getCore(); + RefCounted searcher = core.getSearcher(); + Similarity sim = searcher.get().getSimilarityProvider().get("sim3text"); + assertEquals(MockConfigurableSimilarity.class, sim.getClass()); + assertEquals("I am your default sim", ((MockConfigurableSimilarity)sim).getPassthrough()); + searcher.decref(); + } + + /** ... and for a dynamic field */ + public void testDefaultsDynamic() throws Exception { + SolrCore core = h.getCore(); + RefCounted searcher = core.getSearcher(); + Similarity sim = searcher.get().getSimilarityProvider().get("text_sim3"); + assertEquals(MockConfigurableSimilarity.class, sim.getClass()); + assertEquals("I am your default sim", ((MockConfigurableSimilarity)sim).getPassthrough()); + searcher.decref(); + } + + /** test a field that does not exist */ + public void testNonexistent() throws Exception { + SolrCore core = h.getCore(); + RefCounted searcher = core.getSearcher(); + Similarity sim = searcher.get().getSimilarityProvider().get("sdfdsfdsfdswr5fsdfdsfdsfs"); + assertEquals(MockConfigurableSimilarity.class, sim.getClass()); + assertEquals("I am your default sim", ((MockConfigurableSimilarity)sim).getPassthrough()); + searcher.decref(); + } +}