Create your own Solr Function Query

Solr's Function Query has a lot of useful things,

https://wiki.apache.org/solr/FunctionQuery#Available_Functions

I think that the above functions alone may not meet the requirements.

This time, I will create a FunctionQuery by assuming that the JSON format character string registered in a certain field is parsed, some processing is performed, and then the result is returned.

This article http://www.lifull.blog/entry/2014/02/25/145220 I created it with reference to. The above article is enough to be helpful, so I think some people can make their own. In this article, we will take a closer look at the Solr source code and create it, including explanations of what kind of class structure it has.

How are the default functions implemented?

First, refer to the implementation of the function provided by default.

https://github.com/apache/lucene-solr/blob/master/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java The provided FunctionQuery is implemented as an object of ValueSourceParser class in the above source.

The ValueSourceParser class has the following signatures:

public abstract class ValueSourceParser implements NamedListInitializedPlugin {
  /**
   * Initialize the plugin.
   */
  @Override
  public void init(NamedList args) {}

  /**
   * Parse the user input into a ValueSource.
   */
  public abstract ValueSource parse(FunctionQParser fp) throws SyntaxError;

  ...

Also, in the initialization block </ span> of the ValueSourceParser class

addParser("abs", new ValueSourceParser() {
  @Override
  public ValueSource parse(FunctionQParser fp) throws SyntaxError {
    ValueSource source = fp.parseValueSource();
    return new SimpleFloatFunction(source) {
      @Override
      protected String name() {
        return "abs";
      }

      @Override
      protected float func(int doc, FunctionValues vals) {
        return Math.abs(vals.floatVal(doc));
      }
    };
  }
});

The default function is created like this.

Please note

is.

The definition of SimpleFloatFunction is

https://github.com/apache/lucene-solr/blob/master/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/SimpleFloatFunction.java

It is defined in.

...
/** A simple float function with a single argument
 */
 public abstract class SimpleFloatFunction extends SingleFunction {
  public SimpleFloatFunction(ValueSource source) {
    super(source);
  }

  protected abstract float func(int doc, FunctionValues vals) throws IOException;

  @Override
  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
    final FunctionValues vals =  source.getValues(context, readerContext);
    return new FloatDocValues(this) {
      @Override
      public float floatVal(int doc) throws IOException {
        return func(doc, vals);
      }
      @Override
      public String toString(int doc) throws IOException {
        return name() + '(' + vals.toString(doc) + ')';
      }
    };
  }
...

It reads the abs () argument in a method called getValues and returns an instance of the FloatDocValues class (which inherits from the FunctionValues class). The floatVal method of the FloatDocValues class calls the Overridden func method (#initialize) to call the Math.abs function, which ultimately returns the absolute value.

https://github.com/apache/lucene-solr/blob/master/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/

If you look at some of the classes defined in the above directory, you can see the same.

It is implemented as.

Also, https://wiki.apache.org/solr/SolrPlugins#ValueSourceParser

If you look at the above documentation, you can create your own class and give it a function name by setting it in solrconfig.xml.

From the above, the necessary work is

  1. Create a class that inherits the ValueSourceParser class
  2. Create a class to handle arguments like the SimpleFloatFunction class and override the func method to create and return an instance of a class (such as FloatDocValues) that matches the type of value you want to finally return.

This time I want to create a FunctionQuery that takes a string as an argument and returns a Bool type

https://github.com/apache/lucene-solr/blob/master/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/SimpleBoolFunction.java

It seems that this Simple Bool Function can be used.

/**
 * {@link BoolFunction} implementation which applies an extendible boolean
 * function to the values of a single wrapped {@link ValueSource}.
 *
 * Functions this can be used for include whether a field has a value or not,
 * or inverting the boolean value of the wrapped ValueSource.
 */
public abstract class SimpleBoolFunction extends BoolFunction {
  protected final ValueSource source;

  public SimpleBoolFunction(ValueSource source) {
    this.source = source;
  }

  protected abstract String name();

  protected abstract boolean func(int doc, FunctionValues vals) throws IOException;

  @Override
  public BoolDocValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
    final FunctionValues vals =  source.getValues(context, readerContext);
    return new BoolDocValues(this) {
      @Override
      public boolean boolVal(int doc) throws IOException {
        return func(doc, vals);
      }
      @Override
      public String toString(int doc) throws IOException {
        return name() + '(' + vals.toString(doc) + ')';
      }
    };
  }

If you rewrite the necessary work

  1. Create an original class that inherits the ValueSourceParser class
  2. Create a class that inherits SimpleBoolFunction and override the func method

Will be. Perform the above work to create your own Function Query.

Create an original class that inherits the ValueSourceParser class

package com.overseas_lifull.solr.functionquery;

import org.apache.lucene.queries.function.ValueSource;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.search.SyntaxError;
import org.apache.solr.search.FunctionQParser;
import org.apache.solr.search.ValueSourceParser;

public class PremiumParser extends ValueSourceParser {
    @Override
    public void init(NamedList namedList) {
    }

    @Override
    public ValueSource parse(FunctionQParser fp) throws SyntaxError {
        ValueSource premiumInfo = fp.parseValueSource();

        return new PremiumFunction(premiumInfo);
    }
}

The value of premiumInfo received as an argument is passed to the function called PremiumFunction that will be defined later. This PremiumFunction is a class that inherits SimpleBoolFunction.

Create a class that inherits SimpleBoolFunction and override the func method

package com.overseas_lifull.solr.functionquery;

import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.valuesource.SimpleBoolFunction;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.Date;

public class PremiumFunction extends SimpleBoolFunction {

    public PremiumFunction(ValueSource source) {
        super(source);
    }

    protected String name() {
        return "isPremium";
    }

    protected boolean func(int doc, FunctionValues vals) {
        boolean result = false;
        String jsonStr = vals.strVal(doc); //Get the argument of the isPremium function as a string
        /*Process string and true,Returns false
         * ...Various processing*/
        if (/*Judgment condition*/) {
          return true;
        } else {
          return false;
        }
    }
}

Compile and place it in the directory that Solr expects, and edit solrconfig.xml.

compile

$ javac -classpath "./lib/*:/opt/solr-5.5.3/server/solr-webapp/webapp/WEB-INF/lib/lucene-core-5.5.3.jar:/opt/solr-5.5.3/server/solr-webapp/webapp/WEB-INF/lib/lucene-queries-5.5.3.jar:/opt/solr-5.5.3/server/solr-webapp/webapp/WEB-INF/lib/lucene-queryparser-5.5.3.jar:/opt/solr-5.5.3/server/solr-webapp/webapp/WEB-INF/lib/solr-core-5.5.3.jar:/opt/solr-5.5.3/server/solr-webapp/webapp/WEB-INF/lib/solr-solrj-5.5.3.jar" com/overseas_lifull/solr/functionquery/PremiumFunction.java com/overseas_lifull/solr/functionquery/PremiumParser.java com/overseas_lifull/solr/functionquery/PremiumData.java

The classpath you specify depends on the class you use and the location of the source.

JAR file creation

$ jar cvf premiumQueryFunction.jar ./com/overseas_lifull/solr/functionquery/PremiumData.class ./com/overseas_lifull/solr/functionquery/PremiumData.java ./com/overseas_lifull/solr/functionquery/PremiumFunction.class ./com/overseas_lifull/solr/functionquery/PremiumFunction.java ./com/overseas_lifull/solr/functionquery/PremiumParser.class ./com/overseas_lifull/solr/functionquery/PremiumParser.java

JAR file placement

Create a lib directory in the Solr core directory and place it there.

The directory structure should look like the one below.

conf/
  admin-extra.html
  admin-extra.menu-bottom.html
  admin-extra.menu-top.html
  data-config.xml
  dataimport.properties
  elevate.xml
  schema.xml
  solrconfig.xml
lib/
  premiumQueryFunction.jar
core.properties

Edit solrconfig.xml

<valueSourceParser name="isPremium" class="com.overseas_lifull.solr.functionquery.PremiumParser" />

Restart Solr

Restart Solr to load the edited solrconfig.xml and the placed jar file.

Try to throw a query

http://localhost:8983/solr/my_core/select?q=*:*&fl=*,isPremium(flat_premium_data)&wt=json&indent=true

In the document returned as a response ʻIsPremium (flat_premium_data): Success if there is a field and value (true or false) like true`.

Recommended Posts

Create your own Solr Function Query
Create your own Java annotations
Create your own encode for String.getBytes ()
Create your own validator with Bean Validation
Utilization of Talend component (5) Create your own component
Create your own Android app for Java learning
Create your own Utility with Thymeleaf with Spring Boot
Make your own pomodoro
Make your own Rails validate
Tune your query with EXPLAIN
Make your own Elasticsearch plugin
Create a name input function
How to create your own Controller corresponding to / error with Spring Boot