Package com.senseidb.search.relevance.impl

Source Code of com.senseidb.search.relevance.impl.CompilationHelper

/**
* This software is licensed to you under the Apache License, Version 2.0 (the
* "Apache License").
*
* LinkedIn's contributions are made under the Apache License. If you contribute
* to the Software, the contributions will be deemed to have been made under the
* Apache License, unless you expressly indicate otherwise. Please do not make any
* contributions that would be inconsistent with the Apache License.
*
* You may obtain a copy of the Apache License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, this software
* distributed under the Apache License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Apache
* License for the specific language governing permissions and limitations for the
* software governed under the Apache License.
*
* © 2012 LinkedIn Corp. All Rights Reserved. 
*/
package com.senseidb.search.relevance.impl;

import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet;
import it.unimi.dsi.fastutil.floats.FloatOpenHashSet;
import it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.senseidb.search.relevance.ExternalRelevanceDataStorage;
import com.senseidb.search.req.ErrorType;

import javassist.CannotCompileException;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;

/**

  "relevance": {

      // (1) Model definition part; this json is used to define a model (input variables,
      //     columns/facets, and function parameters and body);

      "model": {
          "variables": {
              "set_int":["c","d"],   // supported hashset types: [set_int, set_float, set_string,
                                     // set_double, set_long]
              "map_int_float":["j"], // supported hashmap: [map_int_float, map_int_double, map_int_*...]
                                     //                    [map_string_int, map_string_float, map_string_*]
              "int":["e","f"],       // supported normal variables: [int, double, float, long, bool, string]
              "long":["g","h"],
              "custom_obj":["krati","big_cache"]   // supported external static big in-memory object (initialized when senseidb starts)
          },

          "facets": {
              "int":["year","age"],  // facet type support: [double, float, int, long, short, string];
              "long":["time"]        // facet variable has the same name as the facet name, and they
                                     // are defined inside this json;
          },

          // (2) Scoring function and function input parameters in Java; A scoring function and its
          //     parameters are the model. A model changes when the function body or signature
          //     changes;

          // Params for the function.  The symbol order matters, and symbols must be those defined
          // above. innerScore MUST be used, otherwise, makes no sense to use the custom relevance;
          // reserved keyword for internal parameters are: "_INNER_SCORE" and "_NOW"

          "function_params":["_INNER_SCORE", "timeVal", "_timeWeight", "_waterworldWeight", "_half_time"],

          // The value string in the following JSONObject is like this (a return statement MUST
          // appear as the last one):
          //
          //    float delta = System.currentTimeMillis() - timeVal;
          //    float t = delta>0 ? delta : 0;
          //    float hour = t/(1000*3600);
          //    float timeScore = (float) Math.exp(-(hour/_half_time));
          //    float waterworldScore = _INNER_SCORE;
          //    float time = timeScore * _timeWeight;
          //    float water = waterworldScore  * _waterworldWeight;
          //    return  (time + water);

          "function":"A LONG JAVA CODE STRING HERE, ONLY AS FUNCTION BODY, NEEDS RETURN STATEMENT."
      }

      // (3) Input values for the model above, if the model requires input values;

      "values": {
          "c":[1996,1997],
          "e":0.98,
          "j":{"1":2.3, "2":3.4, "3":2.9}      // A user input hashmap;
          "jj":{"key":[1,2,3], "value":[2.3, 3.4, 2.9]}      //  It also supports this method to pass in a map.
      }
  }

  A dummy testing relevance json inside a query request json may look
  like this:

  {
      "query": {
          "query_string": {
              "query": "",
              "relevance":{

                  "model":{
                      "variables":{
                           "set_int":["goodYear"],
                           "int":["thisYear"],
                           "string":["coolTag"],
                           "map_int_float":["mileageWeight"],
                           "map_int_string":["yearcolor"],
                           "map_string_float":["colorweight"],
                           "map_string_string":["categorycolor"]
                          },
                      "facets":{
                           "int":["year","mileage"],
                           "long":["groupid"],
                           "string":["color","category"],
                           "mstring":["tags"]
                          },
                      "function_params":["_INNER_SCORE",
                                         "thisYear",
                                         "year",
                                         "goodYear",
                                         "mileageWeight",
                                         "mileage",
                                         "color",
                                         "yearcolor",
                                         "colorweight",
                                         "category",
                                         "categorycolor"],
                      "function":"  if (tags.contains(coolTag))                         \
                                      return 999999f;                                   \
                                    if (categorycolor.containsKey(category)             \
                                        && categorycolor.get(category).equals(color))   \
                                      return 10000f;                                    \
                                    if (colorweight.containsKey(color))                 \
                                      return 200f + colorweight.getFloat(color);        \
                                    if (yearcolor.containsKey(year) &&                  \
                                        yearcolor.get(year).equals(color))              \
                                      return 200f;                                      \
                                    if (mileageWeight.containsKey(mileage))             \
                                      return 10000+mileageWeight.get(mileage);          \
                                    if (goodYear.contains(year))                        \
                                      return (float)Math.exp(2d);                       \
                                    if (year == thisYear)                               \
                                      return 87f;                                       \
                                    return  _INNER_SCORE;"
                  },

                  "values":{
                       "goodYear":[1996,1997],
                       "thisYear":2001,
                       "mileageWeight":{"11400":777.9, "11000":10.2},
                      "yearcolor":{"1998":"red"},
                      "colorweight":{"red":335.5},
                      "categorycolor":{"compact":"red"},
                      "coolTag":"cool"
                  }
              }
          }
      },
      "from": 0,
      "size": 6,
      "explain": false,
      "fetchStored": false,
      "sort":["_score"]
  }

  An advanded usage of weighted multi-facet relevance can be:

  {
      "query": {
          "query_string": {
              "query": "java",
              "relevance": {
                  "model": {
                      "variables": {
                          "string":["skill"]
                       },
                      "facets": {
                          "wmstring":["user_skills"]
                      },
                      "function_params":["_INNER_SCORE",
                                         "user_skills",
                                         "skill"],
                      "function":" int weight = 0;                      \
                                   if (user_skills.hasWeight(skill))    \
                                     weight = user_skills.getWeight();  \
                                   return _INNER_SCORE + weight;"
                  },
                  "values": {
                      "skill":"java"
                  }
               }
          }
      },
      "selections": [
      {
          "terms": {
              "country_code": {
                  "values": ["us"],
                  "excludes": [],
                  "operator": "or"
              }
          }
      }],
      "from": 0,
      "size": 10,
      "explain": false,
      "fetchStored": false
  }

*/

public class CompilationHelper
{
  private static Logger logger = Logger.getLogger(CompilationHelper.class);

  private static ClassPool pool = ClassPool.getDefault();
  // White list of safe classes
  private static HashSet<String> hs_safe = new HashSet<String>();

  // Format strings for relevance model parameters
  private static String[] PARAM_FORMAT_STRINGS = new String[]
  {
    /*  0 */ "  int %s = ints[%d];",
    /*  1 */ "  long %s = longs[%d];",
    /*  2 */ "  double %s = doubles[%d];",
    /*  3 */ "  float %s = floats[%d];",
    /*  4 */ "  String %s = strings[%d];",
    /*  5 */ "  short %s = shorts[%d];",
    /*  6 */ "  boolean %s = booleans[%d];",
    /*  7 */ "  it.unimi.dsi.fastutil.ints.IntOpenHashSet %s = (it.unimi.dsi.fastutil.ints.IntOpenHashSet) sets[%d];",
    /*  8 */ "  it.unimi.dsi.fastutil.longs.LongOpenHashSet %s = (it.unimi.dsi.fastutil.longs.LongOpenHashSet) sets[%d];",
    /*  9 */ "  it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet %s = (it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet) sets[%d];",
    /* 10 */ "  it.unimi.dsi.fastutil.floats.FloatOpenHashSet %s = (it.unimi.dsi.fastutil.floats.FloatOpenHashSet) sets[%d];",
    /* 11 */ "  it.unimi.dsi.fastutil.objects.ObjectOpenHashSet %s = (it.unimi.dsi.fastutil.objects.ObjectOpenHashSet) sets[%d];",
    /* 12 */ "  it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap %s = (it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap) maps[%d];",
    /* 13 */ "  it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap %s = (it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap) maps[%d];",
    /* 14 */ "  it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap %s = (it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap) maps[%d];",
    /* 15 */ "  it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap %s = (it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap) maps[%d];",
    /* 16 */ "  it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap %s = (it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap) maps[%d];",
    /* 17 */ "  it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap %s = (it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap) maps[%d];",
    /* 18 */ "  it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap %s = (it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap) maps[%d];",
    /* 19 */ "  it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap %s = (it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap) maps[%d];",
    /* 20 */ "  it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap %s = (it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap) maps[%d];",
    /* 21 */ "  it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap %s = (it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap) maps[%d];",
    /* 22 */ "  com.senseidb.search.relevance.impl.MFacetInt %s = mFacetInts[%d];",
    /* 23 */ "  com.senseidb.search.relevance.impl.MFacetLong %s = mFacetLongs[%d];",
    /* 24 */ "  com.senseidb.search.relevance.impl.MFacetDouble %s = mFacetDoubles[%d];",
    /* 25 */ "  com.senseidb.search.relevance.impl.MFacetFloat %s = mFacetFloats[%d];",
    /* 26 */ "  com.senseidb.search.relevance.impl.MFacetString %s = mFacetStrings[%d];",
    /* 27 */ "  com.senseidb.search.relevance.impl.MFacetShort %s = mFacetShorts[%d];",
    /* 28 */ "  com.senseidb.search.relevance.impl.WeightedMFacetInt %s = (com.senseidb.search.relevance.impl.WeightedMFacetInt) mFacetInts[%d];",
    /* 29 */ "  com.senseidb.search.relevance.impl.WeightedMFacetLong %s = (com.senseidb.search.relevance.impl.WeightedMFacetLong) mFacetLongs[%d];",
    /* 30 */ "  com.senseidb.search.relevance.impl.WeightedMFacetDouble %s = (com.senseidb.search.relevance.impl.WeightedMFacetDouble) mFacetDoubles[%d];",
    /* 31 */ "  com.senseidb.search.relevance.impl.WeightedMFacetFloat %s = (com.senseidb.search.relevance.impl.WeightedMFacetFloat) mFacetFloats[%d];",
    /* 32 */ "  com.senseidb.search.relevance.impl.WeightedMFacetString %s = (com.senseidb.search.relevance.impl.WeightedMFacetString) mFacetStrings[%d];",
    /* 33 */ "  com.senseidb.search.relevance.impl.WeightedMFacetShort %s = (com.senseidb.search.relevance.impl.WeightedMFacetShort) mFacetShorts[%d];",
    /* 34 */ "  %s %s = (%s) objs[%d];"
  };

  // Map of parameter types to int arrays.  For each parameter type, the
  // int array contains two elements: the first one is the index of
  // PARAM_FORMAT_STRINGS for the parameter type, and the second one is
  // the index of input data array for that parameter.
  private static Map<Integer, int[]> PARAM_INIT_MAP = new HashMap<Integer, int[]>();

  private static int TOTAL_INPUT_DATA_ARRAYS = 16;

  private static String EXP_INT_METHOD    = "public double exp(int val) { return Double.longBitsToDouble(((long) (1512775 * val + 1072632447)) << 32); }";
  private static String EXP_DOUBLE_METHOD = "public double exp(double val) { return Double.longBitsToDouble(((long) (1512775 * val + 1072632447)) << 32); }";
  private static String EXP_FLOAT_METHOD  = "public double exp(float val) { return Double.longBitsToDouble(((long) (1512775 * val + 1072632447)) << 32); }";

  private static String SCORE_METHOD_HEADER =
    "public float score(short[] shorts, " +
    "int[] ints, " +
    "long[] longs, " +
    "float[] floats, " +
    "double[] doubles, " +
    "boolean[] booleans, " +
    "String[] strings, " +
    "Set[] sets, " +
    "Map[] maps, " +
    "com.senseidb.search.relevance.impl.MFacetInt[] mFacetInts, " +
    "com.senseidb.search.relevance.impl.MFacetLong[] mFacetLongs, " +
    "com.senseidb.search.relevance.impl.MFacetFloat[] mFacetFloats, " +
    "com.senseidb.search.relevance.impl.MFacetDouble[] mFacetDoubles, " +
    "com.senseidb.search.relevance.impl.MFacetShort[] mFacetShorts, " +
    "com.senseidb.search.relevance.impl.MFacetString[] mFacetStrings, " +
    "java.lang.Object[] objs)";

  static
  {
    hs_safe.add("it.unimi.dsi.fastutil.ints.IntOpenHashSet");
    hs_safe.add("it.unimi.dsi.fastutil.longs.LongOpenHashSet");
    hs_safe.add("it.unimi.dsi.fastutil.shorts.ShortOpenHashSet");
    hs_safe.add("it.unimi.dsi.fastutil.booleans.BooleanOpenHashSet");
    hs_safe.add("it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet");
    hs_safe.add("it.unimi.dsi.fastutil.floats.FloatOpenHashSet");
    hs_safe.add("it.unimi.dsi.fastutil.objects.ObjectOpenHashSet");

    hs_safe.add("it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap");
    hs_safe.add("it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap");
    hs_safe.add("it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap");
    hs_safe.add("it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap");
    hs_safe.add("it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap");

    hs_safe.add("it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap");
    hs_safe.add("it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap");
    hs_safe.add("it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap");
    hs_safe.add("it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap");
    hs_safe.add("it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap");

    hs_safe.add("it.unimi.dsi.fastutil.objects.AbstractObject2FloatMap");
    hs_safe.add("it.unimi.dsi.fastutil.objects.AbstractObject2FloatFunction");


    hs_safe.add("com.senseidb.search.relevance.impl.MFacet");
    hs_safe.add("com.senseidb.search.relevance.impl.MFacetDouble");
    hs_safe.add("com.senseidb.search.relevance.impl.MFacetFloat");
    hs_safe.add("com.senseidb.search.relevance.impl.MFacetInt");
    hs_safe.add("com.senseidb.search.relevance.impl.MFacetLong");
    hs_safe.add("com.senseidb.search.relevance.impl.MFacetShort");
    hs_safe.add("com.senseidb.search.relevance.impl.MFacetString");

    hs_safe.add("com.senseidb.search.relevance.impl.WeightedMFacet");
    hs_safe.add("com.senseidb.search.relevance.impl.WeightedMFacetDouble");
    hs_safe.add("com.senseidb.search.relevance.impl.WeightedMFacetFloat");
    hs_safe.add("com.senseidb.search.relevance.impl.WeightedMFacetInt");
    hs_safe.add("com.senseidb.search.relevance.impl.WeightedMFacetLong");
    hs_safe.add("com.senseidb.search.relevance.impl.WeightedMFacetShort");
    hs_safe.add("com.senseidb.search.relevance.impl.WeightedMFacetString");
   
    hs_safe.add("java.util.Random");

    pool.importPackage("java.util");
    for (String cls: hs_safe)
    {
      pool.importPackage(cls);
    }
    pool.insertClassPath(new ClassClassPath(CompilationHelper.class));

    hs_safe.add("com.senseidb.search.relevance.impl.CustomMathModel");
    hs_safe.add("com.senseidb.search.relevance.impl.CompilationHelper$CustomLoader");

    hs_safe.add("java.lang.Object");
    hs_safe.add("java.lang.Exception");
    hs_safe.add("java.lang.Boolean");
    hs_safe.add("java.lang.Byte");
    hs_safe.add("java.lang.Double");
    hs_safe.add("java.lang.Float");
    hs_safe.add("java.lang.Integer");
    hs_safe.add("java.lang.Long");
    hs_safe.add("java.lang.Math");
    hs_safe.add("java.lang.Number");
    hs_safe.add("java.lang.Short");
    hs_safe.add("java.lang.String");
    hs_safe.add("java.lang.StringBuffer");
    hs_safe.add("java.lang.StringBuilder");
    hs_safe.add("java.math.BigDecimal");
    hs_safe.add("java.math.BigInteger");
    hs_safe.add("java.math.MathContext");
    hs_safe.add("java.math.RoundingMode");

    //  Index of input data index for different types:
    //
    //  0  int_index        6  m_int_index       12  boolean_index
    //  1  long_index       7  m_long_index      13  set_index
    //  2  double_index     8  m_double_index    14  map_index
    //  3  float_index      9  m_float_index     15  obj_index
    //  4  string_index    10  m_string_index
    //  5  short_index     11  m_short_index

    // the first int in the following int[] is the index of the string template above (PARAM_FORMAT_STRINGS);
    // the second int in the int[] below is the index of the function parameters;
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_INNER_SCORE,       new int[]{ 33});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_NOW,               new int[]{ 11});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_INT,               new int[]{ 00});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_LONG,              new int[]{ 11});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_DOUBLE,            new int[]{ 22});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FLOAT,             new int[]{ 33});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_STRING,            new int[]{ 44});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_BOOLEAN,           new int[]{ 6, 12});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_INT,         new int[]{ 00});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_LONG,        new int[]{ 11});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_DOUBLE,      new int[]{ 22});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_FLOAT,       new int[]{ 33});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_STRING,      new int[]{ 44});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_SHORT,       new int[]{ 55});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_SET_INT,           new int[]{ 7, 13});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_SET_LONG,          new int[]{ 8, 13});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_SET_DOUBLE,        new int[]{ 9, 13});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_SET_FLOAT,         new int[]{10, 13});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_SET_STRING,        new int[]{11, 13});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_MAP_INT_INT,       new int[]{12, 14});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_MAP_INT_LONG,      new int[]{13, 14});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_MAP_INT_DOUBLE,    new int[]{14, 14});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_MAP_INT_FLOAT,     new int[]{15, 14});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_MAP_INT_STRING,    new int[]{16, 14});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_MAP_STRING_INT,    new int[]{17, 14});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_MAP_STRING_LONG,   new int[]{18, 14});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_MAP_STRING_DOUBLE, new int[]{19, 14});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_MAP_STRING_FLOAT,  new int[]{20, 14});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_MAP_STRING_STRING, new int[]{21, 14});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_M_INT,       new int[]{226});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_M_LONG,      new int[]{237});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_M_DOUBLE,    new int[]{248});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_M_FLOAT,     new int[]{259});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_M_STRING,    new int[]{26, 10});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_M_SHORT,     new int[]{27, 11});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_WM_INT,      new int[]{286});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_WM_LONG,     new int[]{297});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_WM_DOUBLE,   new int[]{308});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_WM_FLOAT,    new int[]{319});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_WM_STRING,   new int[]{32, 10});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_WM_SHORT,    new int[]{33, 11});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_FACET_A_INT,       new int[]{0,   0});
    PARAM_INIT_MAP.put(RelevanceJSONConstants.TYPENUMBER_CUSTOM_OBJ,        new int[]{34, 15});
  }

  private static int MAX_NUM_MODELS  = 10000;
  static HashMap<String, CustomMathModel> hmModels = new HashMap<String, CustomMathModel>();

  public static CustomMathModel createCustomMathScorer(JSONObject jsonModel, DataTable dataTable) throws RelevanceException, JSONException
  {
    CustomMathModel cMathModel = null;

    if(jsonModel == null)
      throw new RelevanceException(ErrorType.JsonParsingError, "No json model is specified.");

    JSONObject jsonVariables = jsonModel.optJSONObject(RelevanceJSONConstants.KW_VARIABLES);
    JSONObject jsonFacets = jsonModel.optJSONObject(RelevanceJSONConstants.KW_FACETS);

    // Process the function body and parameters firstly

    JSONArray jsonFuncParameter = jsonModel.optJSONArray(RelevanceJSONConstants.KW_FUNC_PARAMETERS);
    for(int j=0; j<jsonFuncParameter.length(); j++)
    {
      String paramName = jsonFuncParameter.optString(j);
      dataTable.lls_params.add(paramName);
    }

    dataTable.funcBody = jsonModel.optString(RelevanceJSONConstants.KW_FUNCTION);

    // Process facet variables
    int[] facetIndice = new int[]{0, 0, 0}// store the facetIndex, facetMultiIndex, and activity engine facet Index;
    Iterator<String> it_facet = jsonFacets.keys();
    while(it_facet.hasNext())
    {
      String facetType = it_facet.next();
      JSONArray facetArray = jsonFacets.getJSONArray(facetType);

     try {
         handleFacetSymbols(facetType, facetArray, facetIndice, dataTable);
     } catch (JSONException e) {
         logger.error("JSON facets are " + jsonFacets);
         throw e;
     }
    }

    // Process other variables
    Iterator<String> it_var = jsonVariables.keys();
    while(it_var.hasNext())
    {
      String type = it_var.next();
      JSONArray varArray = jsonVariables.getJSONArray(type);
      for (int i = 0; i < varArray.length(); ++i)
      {
        String symbol = varArray.getString(i);
        if (symbol.equals(RelevanceJSONConstants.KW_INNER_SCORE) ||
            symbol.equals(RelevanceJSONConstants.KW_NOW) ||
            symbol.equals(RelevanceJSONConstants.KW_RANDOM))
        {
          throw new RelevanceException(ErrorType.JsonParsingError, "Internal variable name, " + symbol + ", is reserved.");
        }

        Integer typeNum = RelevanceJSONConstants.VARIABLE_INFO_MAP.get(type);
        if (typeNum == null)
        {
          throw new RelevanceException(ErrorType.JsonParsingError, "Variable type, " + type + ", is not recognized.");
        }
        dataTable.hm_type.put(symbol, typeNum);
      }
    }

    // Add the _NOW variable
    String symbolNow = RelevanceJSONConstants.KW_NOW;
    long now = System.currentTimeMillis();
    dataTable.hm_var.put(symbolNow, now);
    dataTable.hm_type.put(symbolNow, RelevanceJSONConstants.TYPENUMBER_NOW);

    // Add the _INNER_SCORE variable
    String symbolInnerScore = RelevanceJSONConstants.KW_INNER_SCORE;
    dataTable.hm_var.put(symbolInnerScore, symbolInnerScore);
    dataTable.hm_type.put(symbolInnerScore, RelevanceJSONConstants.TYPENUMBER_INNER_SCORE);

    if(dataTable.funcBody == null || dataTable.funcBody.length()==0)
      throw new RelevanceException(ErrorType.JsonParsingError, "No function body found.");

    if(dataTable.funcBody.indexOf("return ")==-1)
      throw new RelevanceException(ErrorType.JsonParsingError, "No return statement in the function body.");


    // Check if all the parameters have been defined
    for(int i=0; i< dataTable.lls_params.size(); i++)
    {
      String symbol = dataTable.lls_params.get(i);
      if( !dataTable.hm_type.containsKey(symbol))
        throw new RelevanceException(ErrorType.JsonParsingError, "function parameter: " + symbol + " was not defined.");

      Integer typeNum = dataTable.hm_type.get(symbol);
      if (typeNum >= RelevanceJSONConstants.TYPENUMBER_FACET_INT && typeNum <= RelevanceJSONConstants.TYPENUMBER_FACET_WM_STRING)
      {
        if( (!dataTable.hm_symbol_facet.containsKey(symbol)) && (!dataTable.hm_symbol_mfacet.containsKey(symbol)))
          throw new RelevanceException(ErrorType.JsonParsingError, "function parameter: " + symbol + " was not defined.");
      }
    }

    dataTable.lls_params = filterParameters(dataTable);

    // Compile the math model below
    String paramString = getParamString(dataTable);
    dataTable.classIDString = dataTable.funcBody + paramString;
    String className = "CRel"+ dataTable.classIDString.hashCode();
    logger.info("Custom relevance math class name is:"+ className);

    if(hmModels.containsKey(className))
    {
      cMathModel = hmModels.get(className);
      logger.info("get math model from hashmap:"+ className);
      return cMathModel;
    }
    else
    {
      synchronized(CompilationHelper.class)
      {
        if(hmModels.containsKey(className))
        {
          cMathModel = hmModels.get(className);
          logger.info("get math model from hashmap:"+ className);
          return cMathModel;
        }

        CtClass ch = CompilationHelper.pool.makeClass(className);
        CtClass ci;
        try
        {
          ci = CompilationHelper.pool.get("com.senseidb.search.relevance.impl.CustomMathModel");
        }
        catch (NotFoundException e)
        {
          logger.info(e.getMessage());
          throw new RelevanceException(e);
        }

        ch.addInterface(ci);
        String functionString = makeFuncString(dataTable);

        addStaticFacilityFields(ch);
        addStaticFacilityMethods(ch);

        CtMethod m;
        try
        {
          m = CtNewMethod.make(functionString, ch);
          ch.addMethod(m);
        }
        catch (CannotCompileException e)
        {
          logger.info(e.getMessage());
          throw new RelevanceException(ErrorType.JsonCompilationError, e.getMessage(), e);
        }

        Class h;
        try
        {
          h = CompilationHelper.pool.toClass(ch, new CompilationHelper.CustomLoader(CompilationHelper.class.getClassLoader(), className));
        }
        catch (CannotCompileException e)
        {
          if(hmModels.containsKey(className))
          {
            cMathModel = hmModels.get(className);
            logger.info("get math model from hashmap:"+ className);
            return cMathModel;
          }
          else
          {
            logger.info(e.getMessage());
            throw new RelevanceException(ErrorType.JsonCompilationError, "Compilation error of json relevance model.", e);
          }
        }

        try
        {
          cMathModel = (CustomMathModel)h.newInstance();
        }
        catch (InstantiationException e)
        {
          logger.info(e.getMessage());
          throw new RelevanceException(ErrorType.JsonCompilationError, "Instantiation exception of relevance object.", e);
        }
        catch (IllegalAccessException e)
        {
          logger.info(e.getMessage());
          throw new RelevanceException(ErrorType.JsonCompilationError, "Instantiation exception of relevance object; Illegal access exception", e);
        }

        if(hmModels.size() > MAX_NUM_MODELS)
          hmModels = new HashMap<String, CustomMathModel>();

        hmModels.put(className, cMathModel);
        logger.info("get math model by compilation:"+ className);
        return cMathModel;
      }
    }
  }

  public static void initializeValues(JSONObject jsonValues, DataTable dataTable) throws JSONException
  {
    HashMap<String, Integer> hm_type = dataTable.hm_type;
    Iterator it = hm_type.keySet().iterator();
    while(it.hasNext())
    {
      String symbol = (String)it.next();
      Integer typeNum = dataTable.hm_type.get(symbol);

      if (typeNum == RelevanceJSONConstants.TYPENUMBER_INNER_SCORE ||
          typeNum == RelevanceJSONConstants.TYPENUMBER_NOW )
        continue;

      if (typeNum >= RelevanceJSONConstants.TYPENUMBER_SET_INT &&
          typeNum <= RelevanceJSONConstants.TYPENUMBER_SET_STRING)
      {
        Set hs = null;
        JSONArray values = jsonValues.optJSONArray(symbol);
        if (values == null)
          throw new JSONException("Variable "+ symbol + " does not have value.");

        switch (typeNum)
        {
        case RelevanceJSONConstants.TYPENUMBER_SET_INT:
          hs = new IntOpenHashSet();
          for (int k = 0; k < values.length(); k++)
          {
            hs.add(values.getInt(k));
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_SET_DOUBLE:
          hs = new DoubleOpenHashSet();
          for (int k = 0; k < values.length(); k++)
          {
            hs.add(values.getDouble(k));
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_SET_FLOAT:
          hs = new FloatOpenHashSet();
          for (int k = 0; k < values.length(); k++)
          {
            hs.add((float) values.getDouble(k));
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_SET_LONG:
          hs = new LongOpenHashSet();
          for (int k = 0; k < values.length(); k++)
          {
            hs.add(values.getLong(k));
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_SET_STRING:
          hs = new ObjectOpenHashSet();
          for (int k = 0; k < values.length(); k++)
          {
            hs.add(values.getString(k));
          }
          break;
        }
        dataTable.hm_var.put(symbol, hs);
      }
      else if (typeNum >= RelevanceJSONConstants.TYPENUMBER_MAP_INT_INT &&
               typeNum <= RelevanceJSONConstants.TYPENUMBER_MAP_STRING_STRING)
      {
        JSONObject values = jsonValues.optJSONObject(symbol);
        if (values == null)
          throw new JSONException("Variable "+ symbol + " does not have value.");

        JSONArray keysList = values.names();
        int keySize = keysList == null ? 0 : keysList.length();
       
        // denote if the map is represented in a way of combination of key jsonarray and value jsonarray;
        boolean isKeyValue = isKeyValueArrayMethod(values);
        JSONArray keysArrayList = null, valuesArrayList = null;
        int keyArraySize, valueArraySize;
        if(isKeyValue)
        {
          keysArrayList = values.optJSONArray(RelevanceJSONConstants.KW_KEY);
          valuesArrayList = values.optJSONArray(RelevanceJSONConstants.KW_VALUE);
         
          if (keysArrayList == null)
            throw new JSONException("Variable " + symbol + " is a map, but does not have a key list.");

          if (valuesArrayList == null)
            throw new JSONException("Variable " + symbol + "is a map, but does not have a value list.");

          keyArraySize = keysArrayList.length();
          valueArraySize = valuesArrayList.length();
          if (keyArraySize != valueArraySize)
            throw new JSONException("Variable " + symbol + ": key size is different from value size, can not convert to a map." );
         
          keySize = keysArrayList.length();
        }

        Map hm = null;
        switch (typeNum)
        {
        case RelevanceJSONConstants.TYPENUMBER_MAP_INT_INT:
          hm = new Int2IntOpenHashMap();
          for (int j = 0; j < keySize; j++)
          {
            if(isKeyValue)
              ((Int2IntOpenHashMap) hm).put(keysArrayList.getInt(j), valuesArrayList.getInt(j));
            else
              ((Int2IntOpenHashMap) hm).put(keysList.getInt(j), values.getInt(keysList.getString(j)));
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_MAP_INT_DOUBLE:
          hm = new Int2DoubleOpenHashMap();
          for (int j = 0; j < keySize; j++)
          {
            if(isKeyValue)
              ((Int2DoubleOpenHashMap) hm).put(keysArrayList.getInt(j), valuesArrayList.getDouble(j));
            else
              ((Int2DoubleOpenHashMap) hm).put(keysList.getInt(j), values.getDouble(keysList.getString(j)));
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_MAP_INT_FLOAT:
          hm = new Int2FloatOpenHashMap();
          for (int j = 0; j < keySize; j++)
          {
            if(isKeyValue)
              ((Int2FloatOpenHashMap) hm).put(keysArrayList.getInt(j), (float) valuesArrayList.getDouble(j));
            else
              ((Int2FloatOpenHashMap) hm).put(keysList.getInt(j), (float) values.getDouble(keysList.getString(j)));
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_MAP_INT_LONG:
          hm = new Int2LongOpenHashMap();
          for (int j = 0; j < keySize; j++)
          {
            if(isKeyValue)
              ((Int2LongOpenHashMap) hm).put(keysArrayList.getInt(j), Long.parseLong(valuesArrayList.getString(j)));
            else
              ((Int2LongOpenHashMap) hm).put(keysList.getInt(j), Long.parseLong(values.getString(keysList.getString(j))));
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_MAP_INT_STRING:
          hm = new Int2ObjectOpenHashMap();
          for (int j = 0; j < keySize; j++)
          {
            if(isKeyValue)
              ((Int2ObjectOpenHashMap) hm).put(keysArrayList.getInt(j), valuesArrayList.getString(j));
            else
              ((Int2ObjectOpenHashMap) hm).put(keysList.getInt(j), values.getString(keysList.getString(j)));
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_MAP_STRING_INT:
          hm = new Object2IntOpenHashMap();
          for (int j = 0; j < keySize; j++)
          {
            if(isKeyValue)
              ((Object2IntOpenHashMap) hm).put(keysArrayList.getString(j), valuesArrayList.getInt(j));
            else
            {
              String key = keysList.getString(j);
              ((Object2IntOpenHashMap) hm).put(key, values.getInt(key));
            }
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_MAP_STRING_DOUBLE:
          hm = new Object2DoubleOpenHashMap();
          for (int j = 0; j < keySize; j++)
          {
            if(isKeyValue)
              ((Object2DoubleOpenHashMap) hm).put(keysArrayList.getString(j), valuesArrayList.getDouble(j));
            else
            {
            String key = keysList.getString(j);
            ((Object2DoubleOpenHashMap) hm).put(key, values.getDouble(key));
            }
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_MAP_STRING_FLOAT:
          hm = new Object2FloatOpenHashMap();
          for (int j = 0; j < keySize; j++)
          {
            if(isKeyValue)
              ((Object2FloatOpenHashMap) hm).put(keysArrayList.getString(j), (float) valuesArrayList.getDouble(j));
            else
            {
              String key = keysList.getString(j);
              ((Object2FloatOpenHashMap) hm).put(key, (float) values.getDouble(key));
            }
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_MAP_STRING_LONG:
          hm = new Object2LongOpenHashMap();
          for (int j = 0; j < keySize; j++)
          {
            if(isKeyValue)
              ((Object2LongOpenHashMap) hm).put(keysArrayList.getString(j), Long.parseLong(valuesArrayList.getString(j)));
            else
            {
              String key = keysList.getString(j);
              ((Object2LongOpenHashMap) hm).put(key, Long.parseLong(values.getString(keysList.getString(j))));
            }
          }
          break;
        case RelevanceJSONConstants.TYPENUMBER_MAP_STRING_STRING:
          hm = new Object2ObjectOpenHashMap();
          for (int j = 0; j < keySize; j++)
          {
            if(isKeyValue)
              ((Object2ObjectOpenHashMap) hm).put(keysArrayList.getString(j), valuesArrayList.getString(j));
            else
            {
              String key = keysList.getString(j);
              ((Object2ObjectOpenHashMap) hm).put(key, values.getString(key));
            }
          }
          break;
        }
        dataTable.hm_var.put(symbol, hm);
      }
      else if (typeNum >= RelevanceJSONConstants.TYPENUMBER_INT &&
               typeNum <= RelevanceJSONConstants.TYPENUMBER_STRING)
      {
        if (!jsonValues.has(symbol))
          throw new JSONException("Symbol " + symbol + " was not assigned with a value." );

        switch (typeNum)
        {
        case RelevanceJSONConstants.TYPENUMBER_INT:
          dataTable.hm_var.put(symbol, jsonValues.getInt(symbol));
          break;
        case RelevanceJSONConstants.TYPENUMBER_DOUBLE:
          dataTable.hm_var.put(symbol, jsonValues.getDouble(symbol));
          break;
        case RelevanceJSONConstants.TYPENUMBER_FLOAT:
          dataTable.hm_var.put(symbol, (float) jsonValues.getDouble(symbol));
          break;
        case RelevanceJSONConstants.TYPENUMBER_LONG:
          dataTable.hm_var.put(symbol, Long.parseLong(jsonValues.getString(symbol)));
          break;
        case RelevanceJSONConstants.TYPENUMBER_STRING:
          dataTable.hm_var.put(symbol, jsonValues.getString(symbol));
          break;
        case RelevanceJSONConstants.TYPENUMBER_BOOLEAN:
          dataTable.hm_var.put(symbol, jsonValues.getBoolean(symbol));
          break;
        }
      }
      else if (typeNum == RelevanceJSONConstants.TYPENUMBER_CUSTOM_OBJ)
      {
        Object obj = ExternalRelevanceDataStorage.getObj(symbol);
        if(obj != null)
          dataTable.hm_var.put(symbol, obj);
        else
          throw new JSONException("function parameter: " + symbol + " can not be initialized as a custom Object.");
      }
    }

    // Check if all the parameters have been initialized
    for(int i=0; i< dataTable.lls_params.size(); i++)
    {
      String symbol = dataTable.lls_params.get(i);
      Integer typeNum = dataTable.hm_type.get(symbol);
      if (typeNum < RelevanceJSONConstants.TYPENUMBER_FACET_INT ||
          typeNum > RelevanceJSONConstants.TYPENUMBER_FACET_A_INT)
      {
        if(!dataTable.hm_var.containsKey(symbol))
          throw new JSONException("function parameter: " + symbol + " was not initialized.");
      }
    }
  } // End of initializeValues()

  /**
   * check if in the JSON values part the map variable is represented in a json map way or two key and value json array;
   *       "JsonMapway":{"1":2.3, "2":3.4, "3":2.9}      // A user input hashmap;
   *       "KeyValueJsonArrayPairWay":{"key":[1,2,3], "value":[2.3, 3.4, 2.9]}      //  It also supports this method to pass in a map.
   * @param values
   * @return boolean
   */
  private static boolean isKeyValueArrayMethod(JSONObject mapJSON)
  {
    if(mapJSON.has(RelevanceJSONConstants.KW_KEY) && mapJSON.has(RelevanceJSONConstants.KW_VALUE))
    {
      JSONArray keysList = mapJSON.optJSONArray(RelevanceJSONConstants.KW_KEY);
      JSONArray valuesList = mapJSON.optJSONArray(RelevanceJSONConstants.KW_VALUE);
      if(keysList != null && valuesList != null)
        return true;
    }
    return false;
  }

  private static void addStaticFacilityFields(CtClass ch) throws JSONException
  {
    // add a random field in the object;
    CtField f;
    try
    {
      f = CtField.make("public static java.util.Random _RANDOM = new java.util.Random();", ch);
      ch.addField(f);
    }
    catch (CannotCompileException e)
    {
      logger.info(e.getMessage());
      throw new JSONException(e);
    }
  }
 
  private static void addStaticFacilityMethods(CtClass ch) throws JSONException
  {
    addMethod(EXP_INT_METHOD, ch);
    addMethod(EXP_DOUBLE_METHOD, ch);
    addMethod(EXP_FLOAT_METHOD, ch);
  }

  private static void addMethod(String expStr, CtClass ch) throws JSONException
  {
    CtMethod m_exp;
    try
    {
      m_exp = CtNewMethod.make(expStr, ch);
      ch.addMethod(m_exp);
    }
    catch (CannotCompileException e)
    {
      logger.info(e.getMessage());
      throw new JSONException(e);
    }
  }

  private static String getParamString(DataTable dataTable)
  {
    StringBuilder sb = new StringBuilder();
    for(String param : dataTable.lls_params)
    {
      sb.append(param);
      sb.append("#");
      sb.append(dataTable.hm_type.get(param));
      sb.append("#");
    }
    return sb.toString();
  }

  private static LinkedList<String> filterParameters(DataTable dataTable)
  {
    LinkedList<String> lls_new = new LinkedList<String>();
    for(String param : dataTable.lls_params)
    {
      if!(dataTable.funcBody.indexOf(param) == -1))
        lls_new.add(param);
    }
    return lls_new;
  }

  private static <T, V> String mkString(Map<T, V> map) {
    StringBuffer sb = new StringBuffer().append("{");

    int count = 0;
    for(Map.Entry<T, V> entry : map.entrySet())
    {
      if(count++ != 0)
          sb.append(", ");

      sb.append(entry.getKey()).append(": ").append(entry.getValue());
    }

    return sb.append("}").toString();
  }

    private static <T> String mkString(Set<T> set) {
        StringBuffer sb = new StringBuffer().append("[");

        int count = 0;
        for(T elem : set)
        {
            if(count++ != 0)
                sb.append(", ");

            sb.append(elem);
        }

        return sb.append("]").toString();
    }

  private static void handleFacetSymbols(String facetType,
                                         JSONArray facetArray,
                                         int[] facetIndice,
                                         DataTable dataTable)
    throws JSONException
  {
    Integer[] facetInfo = RelevanceJSONConstants.FACET_INFO_MAP.get(facetType);
    if (facetInfo == null)
    {
       String errorString = String.format("Wrong facet type in facet variable definition json: %s. Map contents are %s. Facet array is %s.",
         facetType, mkString(RelevanceJSONConstants.FACET_INFO_MAP), facetArray);


       throw new JSONException(errorString);
    }

    Integer type = facetInfo[0];

    for(int i=0; i < facetArray.length(); i++)
    {
      String facetName = facetArray.getString(i);
      String symbol = facetName;

      if(dataTable.hm_symbol_facet.containsKey(symbol) || dataTable.hm_symbol_mfacet.containsKey(symbol) || dataTable.hm_symbol_afacet.containsKey(symbol))
        throw new JSONException("facet Symbol "+ symbol + " already defined." );

      if(dataTable.hm_facet_index.containsKey(facetName) || dataTable.hm_mfacet_index.containsKey(facetName) || dataTable.hm_afacet_index.containsKey(facetName))
        throw new JSONException("facet name "+ facetName + " already assigned to a symbol." );

      if (facetInfo[1] == 0)
      {
        // This facet is a normal facet;
        dataTable.hm_symbol_facet.put(symbol, facetName);
        dataTable.hm_facet_index.put(facetName, facetIndice[0]);
        facetIndice[0] = facetIndice[0]+1;
      }
      else if (facetInfo[1] == 1)
      {
        // This is a multi-value facet;
        dataTable.hm_symbol_mfacet.put(symbol, facetName);
        dataTable.hm_mfacet_index.put(facetName, facetIndice[1]);
        facetIndice[1] = facetIndice[1]+1;
      }
      else if (facetInfo[1] == 2)
      {
        // This is an activity engine facet;
        dataTable.hm_symbol_afacet.put(symbol, facetName);
        dataTable.hm_afacet_index.put(facetName, facetIndice[2]);
        facetIndice[2] = facetIndice[2]+1;
      }

      dataTable.hm_type.put(symbol, type);
    }
  }

  private static String makeFuncString(DataTable dataTable) throws JSONException
  {
    int[] paramIndices = new int[TOTAL_INPUT_DATA_ARRAYS];
    for (int i = 0; i < TOTAL_INPUT_DATA_ARRAYS; i++)
    {
      paramIndices[i] = 0;
    }

    StringBuffer sb = new StringBuffer();
    sb.append(SCORE_METHOD_HEADER).append(" {");

    dataTable.useInnerScore = false// set using innerscore to false at the beginning; once we see innerscore is used in the function below, it will be set to true;

    for(int i=0; i< dataTable.lls_params.size();i++)
    {
      String paramName = dataTable.lls_params.get(i);

      if(!dataTable.hm_type.containsKey(paramName) || (dataTable.hm_type.get(paramName) == null))
        throw new JSONException("function parameter " + paramName + " is not defined.");

      Integer paramType = dataTable.hm_type.get(paramName);
      int[] paramInfo = PARAM_INIT_MAP.get(paramType);
      if(paramType.intValue() == RelevanceJSONConstants.TYPENUMBER_CUSTOM_OBJ)
      {
        String className = ExternalRelevanceDataStorage.getObjClsName(paramName);
        if(className == null)
          throw new JSONException("Custom external object " + paramName + " is not found.");
       
        String className2 = className.replace('$', '.');
       
        hs_safe.add(className);
        pool.importPackage(className);
       
        sb.append(String.format(PARAM_FORMAT_STRINGS[paramInfo[0]], className2, paramName, className2, paramIndices[paramInfo[1]]++));
      }
      else
        sb.append(String.format(PARAM_FORMAT_STRINGS[paramInfo[0]], paramName, paramIndices[paramInfo[1]]++));
      if (paramType == RelevanceJSONConstants.TYPENUMBER_INNER_SCORE)
      {
        dataTable.useInnerScore = true;
      }
    }

    sb.append(dataTable.funcBody);
    sb.append("}");
    return sb.toString();
  }

  public static class CustomLoader extends ClassLoader {

    private ClassLoader _cl;
    private String _target;

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {

      if(hs_safe.contains(name) || name.equals(_target))
        return _cl.loadClass(name);
      else {
        String message = String.format("Unable to load class %s. Safe classes are %s", name, mkString(hs_safe));
        throw new ClassNotFoundException(message);
      }
    }

    public CustomLoader(ClassLoader cl, String target) {
        _cl = cl;
        _target = target;
    }
  }

  public static class DataTable {

    //dynamic data;
    public HashMap<String, Object> hm_var;

    //static model data;
    public HashMap<String, Integer> hm_type;
    public HashMap<String, String> hm_symbol_facet;
    public HashMap<String, Integer> hm_facet_index;
    public HashMap<String, String> hm_symbol_mfacet;  //multi-facet
    public HashMap<String, Integer> hm_mfacet_index; //multi-facet
    public HashMap<String, String> hm_symbol_afacet;  //activity-facet
    public HashMap<String, Integer> hm_afacet_index; //activity-facet
   

    public LinkedList<String> lls_params;
    public String funcBody = null;
    public String classIDString = null;
    public boolean useInnerScore = true// by default will calculate innerscore value, set this to false will ignore inner score to save time;

    public DataTable(){
      hm_var = new HashMap<String, Object>();
      hm_type = new HashMap<String, Integer>();
      hm_symbol_facet = new HashMap<String, String>();
      hm_facet_index = new HashMap<String, Integer>();
      hm_symbol_mfacet = new HashMap<String, String>()//multi-facet
      hm_mfacet_index = new HashMap<String, Integer>(); //multi-facet
      hm_symbol_afacet = new HashMap<String, String>()//multi-facet
      hm_afacet_index = new HashMap<String, Integer>(); //multi-facet
      lls_params = new LinkedList<String>();
      useInnerScore = true;
    }
  }
}
TOP

Related Classes of com.senseidb.search.relevance.impl.CompilationHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.