Table of contents
Apache Solr is a good fit if you want to build a scalable and robust search engine, and a nice feature that it has is the possibility to use functions in queries to perform different task related to relevancy or scoring. Most of them are straightforward functions that can be used in many situations in terms of computation, like sum(), product(), div() and so more. But you can also create your own functions and import them as a package in Solr.
Overview about Function Queries
Function queries use functions. The functions can be a constant (numeric or string literal), a field, another function or a parameter substitution argument. Function Queries allow you to modify the ranking of a search query in Solr by applying functions to the results.
Function queries are supported by the DisMax, Extended DisMax, and standard query parsers. Functions must be expressed as function calls (for example, sum(a, b) instead of simply a+b).
There are several ways of using function queries in a Solr query:
- Via an explicit query parser that expects function arguments, such func or frange. For example:
q={!func}div(popularity, price)&fq={!frange l=1000}customer_ratings
- In a
sort
expression. For example:
sort=div(popularity, price) desc, score desc
- Add the results of functions as pseudo-fields to documents in query results. For instance, for:
&fl=sum(x, y),id,a,b,c,score&wt=xml
the output would be:
...
<str name="id">foo</str>
<float name="sum(x,y)">40</float>
<float name="score">0.343</float>
...
Custom Solr Function Queries
In this example, we will write a simple function that can extract values from an object based in the key received as a second argument. If the key could not be found, we will return a default value, that can be changed from the third argument.
Our final function call will look like this:
valdict('{"key1": 1, "key2": 2}', "key1", 3) //outputs 1
In order to write a custom Solr Function Query, you'll need first a Parser
class that extends ValueSourceParser
from Apache Lucene package and overwrite the parse method. The method will return the class with the function logic:
public class ExtractValueInDictSourceParser extends ValueSourceParser {
public void init(NamedList namedList) {
}
@Override
public ValueSource parse(FunctionQParser fp) throws SyntaxError {
ValueSource list = fp.parseValueSource();
ValueSource key = fp.parseValueSource();
ValueSource defaultValue = parseDefaultValue(fp);
return new ExtractValueInDictFunction(list, key, defaultValue);
}
private ValueSource parseDefaultValue(FunctionQParser fp) throws SyntaxError {
ValueSource defaultValue;
if (fp.hasMoreArguments()) {
defaultValue = fp.parseValueSource();
} else {
defaultValue = new LiteralValueSource("0");
}
return defaultValue;
}
}
In solrconfig.xml
, register the ValueSourceParser
directly under the <config>
tag:
<valueSourceParser name="valdict" class="mihaichris.solr.search.ExtractValueInDictSourceParser"/>
And then subclass org.apache.solr.search.ValueSource
for your class that contains function logic:
public class ExtractValueInDictFunction extends ValueSource {
.....
}
To get the values for each Solr document, you must overwrite the getValues method, which is the most important one. The gist of the method is to return a DocValues object, which returns a value given a document ID:
@Override
public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
functionValueList = this.list.getValues(context, readerContext);
functionValueKey = this.key.getValues(context, readerContext);
functionValueDefaultValue = this.defaultValue.getValues(context, readerContext);
return new FloatDocValuesExtension(this);
}
In this example, I extended the FloatDocValues:
private final class FloatDocValuesExtension extends FloatDocValues {
private FloatDocValuesExtension(ValueSource vs) {
super(vs);
}
@Override
public float floatVal(int doc) throws IOException {
return ExtractValueInDictFunction.this.func(doc);
}
}
The return value of the function will be the method type you overwrite. In our case is floatVal which will return a float value for the resulting function.
Finally, you can bundle the package into a .jar and add it to the location you installed Apache Solr, in dist/
directory. You can use Maven to package project:
mvn package
This is my first technical article I wrote ๐. I wanted to be concise about the ideas, and the article to be cohesive. Please send feedback, so I can improve my writing skills.
To learn more about Function Queries, here is the official documentation.
You can find the source code discussed in this article over on GitHub.