mirror of https://github.com/apache/lucene.git
SOLR-2338: improved per-field similarity integration into schema.xml
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1087430 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
004524dbec
commit
74c9b8887f
|
@ -117,6 +117,9 @@ New Features
|
||||||
* SOLR-2396: Add CollationField, which is much more efficient than
|
* SOLR-2396: Add CollationField, which is much more efficient than
|
||||||
the Solr 3.x CollationKeyFilterFactory, and also supports
|
the Solr 3.x CollationKeyFilterFactory, and also supports
|
||||||
Locale-sensitive range queries. (rmuir)
|
Locale-sensitive range queries. (rmuir)
|
||||||
|
|
||||||
|
* SOLR-2338: Add support for using <similarity/> in a schema's fieldType,
|
||||||
|
for customizing scoring on a per-field basis. (hossman, yonik, rmuir)
|
||||||
|
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
|
|
|
@ -43,7 +43,7 @@ import java.io.Serializable;
|
||||||
* by key, so ResponseWriters that output to a format such as JSON will normally
|
* 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
|
* choose a data structure that allows order to be easily preserved in various
|
||||||
* clients (i.e. not a straight map).
|
* 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}
|
* or simply use a regular {@link Map}
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
|
@ -200,10 +200,14 @@ public class NamedList<T> implements Cloneable, Serializable, Iterable<Map.Entry
|
||||||
/**
|
/**
|
||||||
* Gets the value for the first instance of the specified name
|
* Gets the value for the first instance of the specified name
|
||||||
* found.
|
* found.
|
||||||
*
|
* <p>
|
||||||
|
* 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.
|
* @return null if not found or if the value stored was null.
|
||||||
* @see #indexOf
|
* @see #indexOf
|
||||||
* @see #get(String,int)
|
* @see #get(String,int)
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public T get(String name) {
|
public T get(String name) {
|
||||||
return get(name,0);
|
return get(name,0);
|
||||||
|
@ -212,7 +216,10 @@ public class NamedList<T> implements Cloneable, Serializable, Iterable<Map.Entry
|
||||||
/**
|
/**
|
||||||
* Gets the value for the first instance of the specified name
|
* Gets the value for the first instance of the specified name
|
||||||
* found starting at the specified index.
|
* found starting at the specified index.
|
||||||
*
|
* <p>
|
||||||
|
* 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.
|
* @return null if not found or if the value stored was null.
|
||||||
* @see #indexOf
|
* @see #indexOf
|
||||||
*/
|
*/
|
||||||
|
@ -377,6 +384,11 @@ public class NamedList<T> implements Cloneable, Serializable, Iterable<Map.Entry
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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).
|
||||||
|
*/
|
||||||
public T remove(String name) {
|
public T remove(String name) {
|
||||||
int idx = indexOf(name, 0);
|
int idx = indexOf(name, 0);
|
||||||
if(idx != -1) return remove(idx);
|
if(idx != -1) return remove(idx);
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.lucene.document.Field;
|
||||||
import org.apache.lucene.document.Fieldable;
|
import org.apache.lucene.document.Fieldable;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
|
import org.apache.lucene.search.Similarity;
|
||||||
import org.apache.lucene.search.SortField;
|
import org.apache.lucene.search.SortField;
|
||||||
import org.apache.lucene.search.TermQuery;
|
import org.apache.lucene.search.TermQuery;
|
||||||
import org.apache.lucene.search.TermRangeQuery;
|
import org.apache.lucene.search.TermRangeQuery;
|
||||||
|
@ -503,6 +504,34 @@ public abstract class FieldType extends FieldProperties {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @lucene.internal */
|
||||||
|
protected Similarity similarity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Similarity used when scoring fields of this type
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The default implementation returns null, which means this type
|
||||||
|
* has no custom similarity associated with it.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* 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
|
* calls back to TextResponseWriter to write the field value
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,7 +20,9 @@ package org.apache.solr.schema;
|
||||||
import org.apache.lucene.analysis.Analyzer;
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
import org.apache.lucene.analysis.TokenStream;
|
import org.apache.lucene.analysis.TokenStream;
|
||||||
import org.apache.lucene.document.Fieldable;
|
import org.apache.lucene.document.Fieldable;
|
||||||
|
import org.apache.lucene.search.DefaultSimilarity;
|
||||||
import org.apache.lucene.search.IndexSearcher;
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
import org.apache.lucene.search.Similarity;
|
||||||
import org.apache.lucene.search.SimilarityProvider;
|
import org.apache.lucene.search.SimilarityProvider;
|
||||||
import org.apache.lucene.queryParser.QueryParser;
|
import org.apache.lucene.queryParser.QueryParser;
|
||||||
import org.apache.lucene.util.Version;
|
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.SolrException;
|
||||||
import org.apache.solr.common.params.SolrParams;
|
import org.apache.solr.common.params.SolrParams;
|
||||||
import org.apache.solr.common.util.DOMUtil;
|
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.common.util.SystemIdResolver;
|
||||||
import org.apache.solr.core.SolrConfig;
|
import org.apache.solr.core.SolrConfig;
|
||||||
import org.apache.solr.core.Config;
|
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.TokenizerChain;
|
||||||
import org.apache.solr.analysis.TokenizerFactory;
|
import org.apache.solr.analysis.TokenizerFactory;
|
||||||
import org.apache.solr.search.SolrQueryParser;
|
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.AbstractPluginLoader;
|
||||||
import org.apache.solr.util.plugin.SolrCoreAware;
|
import org.apache.solr.util.plugin.SolrCoreAware;
|
||||||
import org.w3c.dom.*;
|
import org.w3c.dom.*;
|
||||||
|
@ -185,19 +189,22 @@ public final class IndexSchema {
|
||||||
*/
|
*/
|
||||||
public Collection<SchemaField> getRequiredFields() { return requiredFields; }
|
public Collection<SchemaField> 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
|
* 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']";
|
expression = "./analyzer[not(@type)] | ./analyzer[@type='index']";
|
||||||
anode = (Node)xpath.evaluate(expression, node, XPathConstants.NODE);
|
anode = (Node)xpath.evaluate(expression, node, XPathConstants.NODE);
|
||||||
Analyzer analyzer = readAnalyzer(anode);
|
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 (queryAnalyzer==null) queryAnalyzer=analyzer;
|
||||||
if (analyzer==null) analyzer=queryAnalyzer;
|
if (analyzer==null) analyzer=queryAnalyzer;
|
||||||
|
@ -394,6 +406,9 @@ public final class IndexSchema {
|
||||||
ft.setAnalyzer(analyzer);
|
ft.setAnalyzer(analyzer);
|
||||||
ft.setQueryAnalyzer(queryAnalyzer);
|
ft.setQueryAnalyzer(queryAnalyzer);
|
||||||
}
|
}
|
||||||
|
if (similarity!=null) {
|
||||||
|
ft.setSimilarity(similarity);
|
||||||
|
}
|
||||||
if (ft instanceof SchemaAware){
|
if (ft instanceof SchemaAware){
|
||||||
schemaAware.add((SchemaAware) ft);
|
schemaAware.add((SchemaAware) ft);
|
||||||
}
|
}
|
||||||
|
@ -491,36 +506,31 @@ public final class IndexSchema {
|
||||||
// stuff it in a normal array for faster access
|
// stuff it in a normal array for faster access
|
||||||
dynamicFields = dFields.toArray(new DynamicField[dFields.size()]);
|
dynamicFields = dFields.toArray(new DynamicField[dFields.size()]);
|
||||||
|
|
||||||
|
|
||||||
Node node = (Node) xpath.evaluate("/schema/similarity", document, XPathConstants.NODE);
|
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) {
|
if (node==null) {
|
||||||
similarityFactory = new SimilarityFactory() {
|
final SolrSimilarityProvider provider = new SolrSimilarityProvider(this);
|
||||||
|
similarityProviderFactory = new SimilarityProviderFactory() {
|
||||||
@Override
|
@Override
|
||||||
public SimilarityProvider getSimilarityProvider() {
|
public SolrSimilarityProvider getSimilarityProvider(IndexSchema schema) {
|
||||||
return IndexSearcher.getDefaultSimilarityProvider();
|
return provider;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
log.debug("using default similarity");
|
log.debug("using default similarityProvider");
|
||||||
} else {
|
} else {
|
||||||
final Object obj = loader.newInstance(((Element) node).getAttribute("class"));
|
final Object obj = loader.newInstance(((Element) node).getAttribute("class"));
|
||||||
if (obj instanceof SimilarityFactory) {
|
// just like always, assume it's a SimilarityProviderFactory and get a ClassCastException - reasonable error handling
|
||||||
// configure a factory, get a similarity back
|
// configure a factory, get a similarity back
|
||||||
SolrParams params = SolrParams.toSolrParams(DOMUtil.childNodesToNamedList(node));
|
NamedList<?> args = DOMUtil.childNodesToNamedList(node);
|
||||||
similarityFactory = (SimilarityFactory)obj;
|
similarityProviderFactory = (SimilarityProviderFactory)obj;
|
||||||
similarityFactory.init(params);
|
similarityProviderFactory.init(args);
|
||||||
} else {
|
if (similarityProviderFactory instanceof SchemaAware){
|
||||||
// just like always, assume it's a SimilarityProvider and get a ClassCastException - reasonable error handling
|
schemaAware.add((SchemaAware) similarityProviderFactory);
|
||||||
similarityFactory = new SimilarityFactory() {
|
|
||||||
@Override
|
|
||||||
public SimilarityProvider getSimilarityProvider() {
|
|
||||||
return (SimilarityProvider) obj;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if (similarityFactory instanceof SchemaAware){
|
log.debug("using similarityProvider factory" + similarityProviderFactory.getClass().getName());
|
||||||
schemaAware.add((SchemaAware) similarityFactory);
|
|
||||||
}
|
|
||||||
log.debug("using similarity factory" + similarityFactory.getClass().getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
node = (Node) xpath.evaluate("/schema/defaultSearchField/text()", document, XPathConstants.NODE);
|
node = (Node) xpath.evaluate("/schema/defaultSearchField/text()", document, XPathConstants.NODE);
|
||||||
|
@ -750,6 +760,30 @@ public final class IndexSchema {
|
||||||
return newArr;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// <analyzer><tokenizer class="...."/><tokenizer class="...." arg="....">
|
// <analyzer><tokenizer class="...."/><tokenizer class="...." arg="....">
|
||||||
//
|
//
|
||||||
|
|
|
@ -16,7 +16,7 @@ package org.apache.solr.schema;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.apache.lucene.search.SimilarityProvider;
|
import org.apache.lucene.search.Similarity;
|
||||||
import org.apache.solr.common.params.SolrParams;
|
import org.apache.solr.common.params.SolrParams;
|
||||||
|
|
||||||
public abstract class SimilarityFactory {
|
public abstract class SimilarityFactory {
|
||||||
|
@ -25,5 +25,5 @@ public abstract class SimilarityFactory {
|
||||||
public void init(SolrParams params) { this.params = params; }
|
public void init(SolrParams params) { this.params = params; }
|
||||||
public SolrParams getParams() { return params; }
|
public SolrParams getParams() { return params; }
|
||||||
|
|
||||||
public abstract SimilarityProvider getSimilarityProvider();
|
public abstract Similarity getSimilarity();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}.
|
||||||
|
* <p>
|
||||||
|
* 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);
|
||||||
|
}
|
|
@ -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.
|
||||||
|
* <p>
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -391,6 +391,32 @@
|
||||||
|
|
||||||
<fieldType name="latLon" class="solr.LatLonType" subFieldType="double"/>
|
<fieldType name="latLon" class="solr.LatLonType" subFieldType="double"/>
|
||||||
|
|
||||||
|
<!-- some per-field similarity examples -->
|
||||||
|
|
||||||
|
<!-- specify a Similarity classname directly -->
|
||||||
|
<fieldType name="sim1" class="solr.TextField">
|
||||||
|
<analyzer>
|
||||||
|
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
|
||||||
|
</analyzer>
|
||||||
|
<similarity class="org.apache.lucene.misc.SweetSpotSimilarity"/>
|
||||||
|
</fieldType>
|
||||||
|
|
||||||
|
<!-- specify a Similarity factory -->
|
||||||
|
<fieldType name="sim2" class="solr.TextField">
|
||||||
|
<analyzer>
|
||||||
|
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
|
||||||
|
</analyzer>
|
||||||
|
<similarity class="org.apache.solr.schema.CustomSimilarityFactory">
|
||||||
|
<str name="echo">is there an echo?</str>
|
||||||
|
</similarity>
|
||||||
|
</fieldType>
|
||||||
|
|
||||||
|
<!-- don't specify any sim at all: get the default -->
|
||||||
|
<fieldType name="sim3" class="solr.TextField">
|
||||||
|
<analyzer>
|
||||||
|
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
|
||||||
|
</analyzer>
|
||||||
|
</fieldType>
|
||||||
</types>
|
</types>
|
||||||
|
|
||||||
|
|
||||||
|
@ -496,6 +522,9 @@
|
||||||
<field name="multiDefault" type="string" indexed="true" stored="true" default="muLti-Default" multiValued="true"/>
|
<field name="multiDefault" type="string" indexed="true" stored="true" default="muLti-Default" multiValued="true"/>
|
||||||
<field name="intDefault" type="int" indexed="true" stored="true" default="42" multiValued="false"/>
|
<field name="intDefault" type="int" indexed="true" stored="true" default="42" multiValued="false"/>
|
||||||
|
|
||||||
|
<field name="sim1text" type="sim1" indexed="true" stored="true"/>
|
||||||
|
<field name="sim2text" type="sim2" indexed="true" stored="true"/>
|
||||||
|
<field name="sim3text" type="sim3" indexed="true" stored="true"/>
|
||||||
|
|
||||||
<field name="tlong" type="tlong" indexed="true" stored="true" />
|
<field name="tlong" type="tlong" indexed="true" stored="true" />
|
||||||
|
|
||||||
|
@ -574,6 +603,10 @@
|
||||||
|
|
||||||
<dynamicField name="*_mfacet" type="string" indexed="true" stored="false" multiValued="true" />
|
<dynamicField name="*_mfacet" type="string" indexed="true" stored="false" multiValued="true" />
|
||||||
|
|
||||||
|
<!-- make sure custom sims work with dynamic fields -->
|
||||||
|
<dynamicField name="*_sim1" type="sim1" indexed="true" stored="true"/>
|
||||||
|
<dynamicField name="*_sim2" type="sim2" indexed="true" stored="true"/>
|
||||||
|
<dynamicField name="*_sim3" type="sim3" indexed="true" stored="true"/>
|
||||||
</fields>
|
</fields>
|
||||||
|
|
||||||
<defaultSearchField>text</defaultSearchField>
|
<defaultSearchField>text</defaultSearchField>
|
||||||
|
@ -607,12 +640,18 @@
|
||||||
<!-- dynamic destination -->
|
<!-- dynamic destination -->
|
||||||
<copyField source="*_dynamic" dest="dynamic_*"/>
|
<copyField source="*_dynamic" dest="dynamic_*"/>
|
||||||
|
|
||||||
<!-- Similarity is the scoring routine for each document vs a query.
|
<!-- expert: SimilarityProvider contains scoring routines that are not field-specific,
|
||||||
A custom similarity may be specified here, but the default is fine
|
such as coord() and queryNorm(). most scoring customization happens in the fieldtype.
|
||||||
|
A custom similarity provider may be specified here, but the default is fine
|
||||||
for most applications.
|
for most applications.
|
||||||
-->
|
-->
|
||||||
<similarity class="org.apache.solr.schema.CustomSimilarityFactory">
|
<similarityProvider class="org.apache.solr.schema.CustomSimilarityProviderFactory">
|
||||||
<str name="echo">is there an echo?</str>
|
<str name="echo">is there an echo?</str>
|
||||||
</similarity>
|
</similarityProvider>
|
||||||
|
|
||||||
|
<!-- default similarity, unless otherwise specified by the fieldType
|
||||||
|
-->
|
||||||
|
<similarity class="org.apache.solr.schema.CustomSimilarityFactory">
|
||||||
|
<str name="echo">I am your default sim</str>
|
||||||
|
</similarity>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.schema;
|
package org.apache.solr.schema;
|
||||||
|
|
||||||
import org.apache.lucene.search.SimilarityProvider;
|
import org.apache.lucene.search.Similarity;
|
||||||
|
|
||||||
public class CustomSimilarityFactory extends SimilarityFactory {
|
public class CustomSimilarityFactory extends SimilarityFactory {
|
||||||
@Override
|
@Override
|
||||||
public SimilarityProvider getSimilarityProvider() {
|
public Similarity getSimilarity() {
|
||||||
return new MockConfigurableSimilarity(params.get("echo"));
|
return new MockConfigurableSimilarity(params.get("echo"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,11 +81,11 @@ public class IndexSchemaTest extends SolrTestCaseJ4 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimilarityFactory() {
|
public void testSimilarityProviderFactory() {
|
||||||
SolrCore core = h.getCore();
|
SolrCore core = h.getCore();
|
||||||
SimilarityProvider similarity = core.getSchema().getSimilarityProvider();
|
SimilarityProvider similarityProvider = core.getSchema().getSimilarityProvider();
|
||||||
assertTrue("wrong class", similarity instanceof MockConfigurableSimilarity);
|
assertTrue("wrong class", similarityProvider instanceof MockConfigurableSimilarityProvider);
|
||||||
assertEquals("is there an echo?", ((MockConfigurableSimilarity)similarity).getPassthrough());
|
assertEquals("is there an echo?", ((MockConfigurableSimilarityProvider)similarityProvider).getPassthrough());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.schema;
|
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;
|
private String passthrough;
|
||||||
|
|
||||||
public MockConfigurableSimilarity(String passthrough) {
|
public MockConfigurableSimilarity(String passthrough) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<SolrIndexSearcher> 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<SolrIndexSearcher> 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<SolrIndexSearcher> 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<SolrIndexSearcher> 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<SolrIndexSearcher> 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<SolrIndexSearcher> 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<SolrIndexSearcher> 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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue