All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.ltr.feature.Feature Maven / Gradle / Ivy

There is a newer version: 9.7.0
Show newest version
/*
 * 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.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.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Weight;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.ltr.DocInfo;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.macro.MacroExpander;
import org.apache.solr.util.SolrPluginUtils;

/**
 * A recipe for computing a feature.  Subclass this for specialized feature calculations.
 * 

* A feature consists of *

    *
  • a name as the identifier *
  • parameters to represent the specific feature *
*

* Example configuration (snippet): *

{
   "class" : "...",
   "name" : "myFeature",
   "params" : {
       ...
   }
}
*

* {@link Feature} is an abstract class and concrete classes should implement * the {@link #validate()} function, and must implement the {@link #paramsToMap()} * and createWeight() methods. */ public abstract class Feature extends Query { final protected String name; private int index = -1; private float defaultValue = 0.0f; final private Map params; public static Feature getInstance(SolrResourceLoader solrResourceLoader, String className, String name, Map params) { final Feature f = solrResourceLoader.newInstance( className, Feature.class, new String[0], // no sub packages new Class[] { String.class, Map.class }, new Object[] { name, params }); if (params != null) { SolrPluginUtils.invokeSetters(f, params.entrySet()); } f.validate(); return f; } public Feature(String name, Map params) { this.name = name; this.params = params; } /** * As part of creation of a feature instance, this function confirms * that the feature parameters are valid. * * @throws FeatureException * Feature Exception */ protected abstract void validate() throws FeatureException; @Override public String toString(String field) { final StringBuilder sb = new StringBuilder(64); // default initialCapacity of 16 won't be enough sb.append(getClass().getSimpleName()); sb.append(" [name=").append(name); final LinkedHashMap params = paramsToMap(); if (params != null) { sb.append(", params=").append(params); } sb.append(']'); return sb.toString(); } public abstract FeatureWeight createWeight(IndexSearcher searcher, boolean needsScores, SolrQueryRequest request, Query originalQuery, Map efi) throws IOException; public float getDefaultValue() { return defaultValue; } public void setDefaultValue(String value){ defaultValue = Float.parseFloat(value); } @Override public int hashCode() { final int prime = 31; int result = classHash(); result = (prime * result) + index; result = (prime * result) + ((name == null) ? 0 : name.hashCode()); result = (prime * result) + ((params == null) ? 0 : params.hashCode()); return result; } @Override public boolean equals(Object o) { return sameClassAs(o) && equalsTo(getClass().cast(o)); } private boolean equalsTo(Feature other) { if (index != other.index) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } if (params == null) { if (other.params != null) { return false; } } else if (!params.equals(other.params)) { return false; } return true; } /** * @return the name */ public String getName() { return name; } /** * @return the id */ public int getIndex() { return index; } /** * @param index * Unique ID for this feature. Similar to feature name, except it can * be used to directly access the feature in the global list of * features. */ public void setIndex(int index) { this.index = index; } public abstract LinkedHashMap paramsToMap(); /** * Weight for a feature **/ public abstract class FeatureWeight extends Weight { final protected IndexSearcher searcher; final protected SolrQueryRequest request; final protected Map efi; final protected MacroExpander macroExpander; final protected Query originalQuery; /** * Initialize a feature without the normalizer from the feature file. This is * called on initial construction since multiple models share the same * features, but have different normalizers. A concrete model's feature is * copied through featForNewModel(). * * @param q * Solr query associated with this FeatureWeight * @param searcher * Solr searcher available for features if they need them */ public FeatureWeight(Query q, IndexSearcher searcher, SolrQueryRequest request, Query originalQuery, Map efi) { super(q); this.searcher = searcher; this.request = request; this.originalQuery = originalQuery; this.efi = efi; macroExpander = new MacroExpander(efi,true); } public String getName() { return Feature.this.getName(); } public int getIndex() { return Feature.this.getIndex(); } public float getDefaultValue() { return Feature.this.getDefaultValue(); } @Override public abstract FeatureScorer scorer(LeafReaderContext context) throws IOException; @Override public boolean isCacheable(LeafReaderContext ctx) { return false; } @Override public Explanation explain(LeafReaderContext context, int doc) throws IOException { final FeatureScorer r = scorer(context); float score = getDefaultValue(); if (r != null) { r.iterator().advance(doc); if (r.docID() == doc) { score = r.score(); } return Explanation.match(score, toString()); }else{ return Explanation.match(score, "The feature has no value"); } } /** * Used in the FeatureWeight's explain. Each feature should implement this * returning properties of the specific scorer useful for an explain. For * example "MyCustomClassFeature [name=" + name + "myVariable:" + myVariable + * "]"; If not provided, a default implementation will return basic feature * properties, which might not include query time specific values. */ @Override public String toString() { return Feature.this.toString(); } @Override public void extractTerms(Set terms) { // no-op } /** * A 'recipe' for computing a feature */ public abstract class FeatureScorer extends Scorer { final protected String name; private DocInfo docInfo; final protected DocIdSetIterator itr; public FeatureScorer(Feature.FeatureWeight weight, DocIdSetIterator itr) { super(weight); this.itr = itr; name = weight.getName(); docInfo = null; } @Override public abstract float score() throws IOException; /** * Used to provide context from initial score steps to later reranking steps. */ public void setDocInfo(DocInfo docInfo) { this.docInfo = docInfo; } public DocInfo getDocInfo() { return docInfo; } @Override public int docID() { return itr.docID(); } @Override public DocIdSetIterator iterator() { return itr; } } /** * Default FeatureScorer class that returns the score passed in. Can be used * as a simple ValueFeature, or to return a default scorer in case an * underlying feature's scorer is null. */ public class ValueFeatureScorer extends FeatureScorer { float constScore; public ValueFeatureScorer(FeatureWeight weight, float constScore, DocIdSetIterator itr) { super(weight,itr); this.constScore = constScore; } @Override public float score() { return constScore; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy