Package com.senseidb.conf

Source Code of com.senseidb.conf.SenseiFacetHandlerBuilder

/**
* 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.conf;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.util.Assert;

import com.browseengine.bobo.facets.FacetHandler;
import com.browseengine.bobo.facets.FacetHandler.FacetDataNone;
import com.browseengine.bobo.facets.FacetHandlerInitializerParam;
import com.browseengine.bobo.facets.RuntimeFacetHandler;
import com.browseengine.bobo.facets.RuntimeFacetHandlerFactory;
import com.browseengine.bobo.facets.AbstractRuntimeFacetHandlerFactory;
import com.browseengine.bobo.facets.attribute.AttributesFacetHandler;
import com.browseengine.bobo.facets.data.PredefinedTermListFactory;
import com.browseengine.bobo.facets.data.TermListFactory;
import com.browseengine.bobo.facets.impl.CompactMultiValueFacetHandler;
import com.browseengine.bobo.facets.impl.DynamicTimeRangeFacetHandler;
import com.browseengine.bobo.facets.impl.HistogramFacetHandler;
import com.browseengine.bobo.facets.impl.MultiValueFacetHandler;
import com.browseengine.bobo.facets.impl.MultiValueWithWeightFacetHandler;
import com.browseengine.bobo.facets.impl.PathFacetHandler;
import com.browseengine.bobo.facets.impl.RangeFacetHandler;
import com.browseengine.bobo.facets.impl.SimpleFacetHandler;
import com.browseengine.bobo.facets.range.MultiRangeFacetHandler;
import com.senseidb.conf.SenseiSchema.FacetDefinition;
import com.senseidb.indexing.DefaultSenseiInterpreter;
import com.senseidb.indexing.activity.ActivityValues;
import com.senseidb.indexing.activity.CompositeActivityManager;
import com.senseidb.indexing.activity.CompositeActivityValues;
import com.senseidb.indexing.activity.facet.ActivityRangeFacetHandler;
import com.senseidb.indexing.activity.primitives.ActivityIntValues;
import com.senseidb.indexing.activity.time.TimeAggregatedActivityValues;
import com.senseidb.plugin.SenseiPluginRegistry;
import com.senseidb.search.facet.UIDFacetHandler;
import com.senseidb.search.plugin.PluggableSearchEngineManager;
import com.senseidb.search.req.SenseiSystemInfo;

public class SenseiFacetHandlerBuilder {

    private static Logger logger = Logger
            .getLogger(SenseiFacetHandlerBuilder.class);

    public static String UID_FACET_NAME = "_uid";

    private static Map<String, TermListFactory<?>> getPredefinedTermListFactoryMap(JSONObject schemaObj) throws JSONException, ConfigurationException {
        HashMap<String, TermListFactory<?>> retMap = new HashMap<String, TermListFactory<?>>();
        JSONObject tableElem = schemaObj.optJSONObject("table");
        if (tableElem == null) {
            throw new ConfigurationException("empty schema");
        }
        JSONArray columns = tableElem.optJSONArray("columns");

        int count = 0;
        if (columns != null) {
            count = columns.length();
        }

        for (int i = 0; i < count; ++i) {
            JSONObject column = columns.getJSONObject(i);
            try {
                String n = column.getString("name");
                String t = column.getString("type");

                TermListFactory<?> factory = null;

                if (t.equals("int")) {
                    factory = DefaultSenseiInterpreter
                            .getTermListFactory(int.class);
                } else if (t.equals("short")) {
                    factory = DefaultSenseiInterpreter
                            .getTermListFactory(short.class);
                } else if (t.equals("long")) {
                    factory = DefaultSenseiInterpreter
                            .getTermListFactory(long.class);
                } else if (t.equals("float")) {
                    factory = DefaultSenseiInterpreter
                            .getTermListFactory(float.class);
                } else if (t.equals("double")) {
                    factory = DefaultSenseiInterpreter
                            .getTermListFactory(double.class);
                } else if (t.equals("char")) {
                    factory = DefaultSenseiInterpreter
                            .getTermListFactory(char.class);
                } else if (t.equals("string")) {
                    factory = TermListFactory.StringListFactory;
                } else if (t.equals("boolean")) {
                    factory = DefaultSenseiInterpreter
                            .getTermListFactory(boolean.class);
                } else if (t.equals("date")) {

                    String f = "";
                    try {
                        f = column.optString("format");
                    } catch (Exception ex) {
                        logger.error(ex.getMessage(), ex);
                    }

                    if (f.isEmpty())
                        throw new Exception("Date format cannot be empty.");
                    else
                        factory = new PredefinedTermListFactory<Date>(Date.class, f);
                }

                if (factory != null) {
                    retMap.put(n, factory);
                }

            } catch (Exception e) {
                throw new ConfigurationException("Error parsing schema: "
                        + column, e);
            }
        }
        return retMap;
    }

    static SimpleFacetHandler buildSimpleFacetHandler(String name,
                                                      String fieldName,
                                                      Set<String> depends,
                                                      TermListFactory<?> termListFactory,
                                                      int invertedIndexPenalty) {
        return new SimpleFacetHandler(name, fieldName, termListFactory, depends, invertedIndexPenalty);
    }

    static CompactMultiValueFacetHandler buildCompactMultiHandler(String name, String fieldName, Set<String> depends, TermListFactory<?> termListFactory) {
        // compact multi should honor depends
        return new CompactMultiValueFacetHandler(name, fieldName, termListFactory);
    }

    static MultiValueFacetHandler buildMultiHandler(String name, String fieldName, TermListFactory<?> termListFactory, Set<String> depends, int invertedIndexPenalty) {
        return new MultiValueFacetHandler(name, fieldName, termListFactory, null, depends, invertedIndexPenalty);
    }

    static MultiValueFacetHandler buildWeightedMultiHandler(String name, String fieldName, TermListFactory<?> termListFactory, Set<String> depends, int invertedIndexPenalty) {
        return new MultiValueWithWeightFacetHandler(name, fieldName, termListFactory, invertedIndexPenalty);
    }

    static PathFacetHandler buildPathHandler(String name, String fieldName, Map<String, List<String>> paramMap, int invertedIndexPenalty) {
        PathFacetHandler handler = new PathFacetHandler(name, fieldName, false);    // path does not support multi value yet
        String sep = null;
        if (paramMap != null) {
            List<String> sepVals = paramMap.get("separator");
            if (sepVals != null && sepVals.size() > 0) {
                sep = sepVals.get(0);
            }
        }
        if (sep != null) {
            handler.setSeparator(sep);
        }
        return handler;
    }

    static RangeFacetHandler buildRangeHandler(String name, String fieldName, TermListFactory<?> termListFactory, Map<String, List<String>> paramMap) {
        LinkedList<String> predefinedRanges = buildPredefinedRanges(paramMap);
        return new RangeFacetHandler(name, fieldName, termListFactory, predefinedRanges);
    }

    private static LinkedList<String> buildPredefinedRanges(Map<String, List<String>> paramMap) {
        LinkedList<String> predefinedRanges = new LinkedList<String>();
        if (paramMap != null) {
            List<String> rangeList = paramMap.get("range");
            if (rangeList != null) {
                for (String range : rangeList) {
                    if (!range.matches("\\[.* TO .*\\]")) {
                        if (!range.contains("-") || !range.contains(",")) {
                            range = "[" + range.replaceFirst("[-,]", " TO ") + "]";
                        } else {
                            range = "[" + range.replaceFirst(",", " TO ") + "]";
                        }
                    }
                    predefinedRanges.add(range);
                }
            }
        }
        return predefinedRanges;
    }

    public static Map<String, List<String>> parseParams(JSONArray paramList) throws JSONException {
        HashMap<String, List<String>> retmap = new HashMap<String, List<String>>();
        if (paramList != null) {
            int count = paramList.length();
            for (int j = 0; j < count; ++j) {
                JSONObject param = paramList.getJSONObject(j);
                String paramName = param.getString("name");
                String paramValue = param.getString("value");

                List<String> list = retmap.get(paramName);
                if (list == null) {
                    list = new LinkedList<String>();
                    retmap.put(paramName, list);
                }

                list.add(paramValue);
            }
        }

        return retmap;
    }

    private static String getRequiredSingleParam(Map<String, List<String>> paramMap,
                                                 String name)
            throws ConfigurationException {
        if (paramMap != null) {
            List<String> vals = paramMap.get(name);
            if (vals != null && vals.size() > 0) {
                return vals.get(0);
            } else {
                throw new ConfigurationException("Parameter " + name + " is missing.");
            }
        } else {
            throw new ConfigurationException("Parameter map is null.");
        }
    }

    private static RuntimeFacetHandlerFactory<?, ?> getHistogramFacetHandlerFactory(JSONObject facet,
                                                                                    String name,
                                                                                    Map<String, List<String>> paramMap)
            throws ConfigurationException {
        String dataType = getRequiredSingleParam(paramMap, "datatype");
        String dataHandler = getRequiredSingleParam(paramMap, "datahandler");
        String startParam = getRequiredSingleParam(paramMap, "start");
        String endParam = getRequiredSingleParam(paramMap, "end");
        String unitParam = getRequiredSingleParam(paramMap, "unit");

        if ("int".equals(dataType)) {
            int start = Integer.parseInt(startParam);
            int end = Integer.parseInt(endParam);
            int unit = Integer.parseInt(unitParam);
            return buildHistogramFacetHandlerFactory(name, dataHandler, start, end, unit);
        } else if ("short".equals(dataType)) {
            short start = (short) Integer.parseInt(startParam);
            short end = (short) Integer.parseInt(endParam);
            short unit = (short) Integer.parseInt(unitParam);
            return buildHistogramFacetHandlerFactory(name, dataHandler, start, end, unit);
        } else if ("long".equals(dataType)) {
            long start = Long.parseLong(startParam);
            long end = Long.parseLong(endParam);
            long unit = Long.parseLong(unitParam);
            return buildHistogramFacetHandlerFactory(name, dataHandler, start, end, unit);
        } else if ("float".equals(dataType)) {
            float start = Float.parseFloat(startParam);
            float end = Float.parseFloat(endParam);
            float unit = Float.parseFloat(unitParam);
            return buildHistogramFacetHandlerFactory(name, dataHandler, start, end, unit);
        } else if ("double".equals(dataType)) {
            double start = Double.parseDouble(startParam);
            double end = Double.parseDouble(endParam);
            double unit = Double.parseDouble(unitParam);
            return buildHistogramFacetHandlerFactory(name, dataHandler, start, end, unit);
        }
        return null;
    }

    private static <T extends Number> RuntimeFacetHandlerFactory<?, ?> buildHistogramFacetHandlerFactory(final String name,
                                                                                                         final String dataHandler,
                                                                                                         final T start,
                                                                                                         final T end,
                                                                                                         final T unit) {
        return new AbstractRuntimeFacetHandlerFactory<FacetHandlerInitializerParam, RuntimeFacetHandler<FacetDataNone>>() {
            @Override
            public RuntimeFacetHandler<FacetDataNone> get(FacetHandlerInitializerParam params) {
                return new HistogramFacetHandler<T>(name, dataHandler, start, end, unit);
            }

            ;

            @Override
            public boolean isLoadLazily() {
                return true;
            }

            @Override
            public String getName() {
                return name;
            }
        };
    }

    public static SenseiSystemInfo buildFacets(JSONObject schemaObj,
                                               SenseiPluginRegistry pluginRegistry,
                                               List<FacetHandler<?>> facets,
                                               List<RuntimeFacetHandlerFactory<?, ?>> runtimeFacets,
                                               PluggableSearchEngineManager pluggableSearchEngineManager,
                                               int invertedIndexPenalty)
            throws JSONException, ConfigurationException {
        Set<String> pluggableSearchEngineFacetNames = pluggableSearchEngineManager.getFacetNames();
        SenseiSystemInfo sysInfo = new SenseiSystemInfo();
        JSONArray facetsList = schemaObj.optJSONArray("facets");

        int count = 0;

        if (facetsList != null) {
            count = facetsList.length();
        }

        if (count <= 0) {
            return sysInfo;
        }

        JSONObject table = schemaObj.optJSONObject("table");
        if (table == null) {
            throw new ConfigurationException("Empty schema");
        }
        JSONArray columns = table.optJSONArray("columns");
        Map<String, JSONObject> columnMap = new HashMap<String, JSONObject>();
        for (int i = 0; i < columns.length(); ++i) {
            JSONObject column = columns.getJSONObject(i);
            try {
                String name = column.getString("name");
                columnMap.put(name, column);
            } catch (Exception e) {
                throw new ConfigurationException("Error parsing schema: ", e);
            }
        }

        Map<String, TermListFactory<?>> termListFactoryMap = getPredefinedTermListFactoryMap(schemaObj);

        Set<SenseiSystemInfo.SenseiFacetInfo> facetInfos = new HashSet<SenseiSystemInfo.SenseiFacetInfo>();
        for (int i = 0; i < count; ++i) {

            JSONObject facet = facetsList.getJSONObject(i);
            try {
                String name = facet.getString("name");
                if (UID_FACET_NAME.equals(name)) {
                    logger.error("facet name: " + UID_FACET_NAME + " is reserved, skipping...");
                    continue;
                }

                String type = facet.getString("type");
                String fieldName = facet.optString("column", name);
                Set<String> dependSet = new HashSet<String>();
                JSONArray dependsArray = facet.optJSONArray("depends");

                if (dependsArray != null) {
                    int depCount = dependsArray.length();
                    for (int k = 0; k < depCount; ++k) {
                        dependSet.add(dependsArray.getString(k));
                    }
                }

                SenseiSystemInfo.SenseiFacetInfo facetInfo = new SenseiSystemInfo.SenseiFacetInfo(name);
                Map<String, String> facetProps = new HashMap<String, String>();
                facetProps.put("type", type);
                facetProps.put("column", fieldName);
                JSONObject column = columnMap.get(fieldName);
                String columnType = (column == null) ? "" : column.optString("type", "");
                if (column != null && column.opt("activity") != null && column.optBoolean("activity")) {
                    columnType = "aint";
                }
                facetProps.put("column_type", columnType);
                facetProps.put("depends", dependSet.toString());

                JSONArray paramList = facet.optJSONArray("params");

                Map<String, List<String>> paramMap = parseParams(paramList);

                for (Entry<String, List<String>> entry : paramMap.entrySet()) {
                    facetProps.put(entry.getKey(), entry.getValue().toString());
                }

                facetInfo.setProps(facetProps);
                facetInfos.add(facetInfo);
                if (pluggableSearchEngineFacetNames.contains(name)) {
                    continue;
                }
                FacetHandler<?> facetHandler = null;
                if (type.equals("simple")) {
                    facetHandler = buildSimpleFacetHandler(name, fieldName, dependSet, termListFactoryMap.get(fieldName), invertedIndexPenalty);
                } else if (type.equals("path")) {
                    facetHandler = buildPathHandler(name, fieldName, paramMap, invertedIndexPenalty);
                } else if (type.equals("range")) {
                    if (column.optBoolean("multi")) {
                        facetHandler = new MultiRangeFacetHandler(name, fieldName, null, termListFactoryMap.get(fieldName),
                                buildPredefinedRanges(paramMap));
                    } else {
                        facetHandler = buildRangeHandler(name, fieldName, termListFactoryMap.get(fieldName), paramMap);
                    }
                } else if (type.equals("multi")) {
                    facetHandler = buildMultiHandler(name, fieldName, termListFactoryMap.get(fieldName), dependSet, invertedIndexPenalty);
                } else if (type.equals("compact-multi")) {
                    facetHandler = buildCompactMultiHandler(name, fieldName, dependSet, termListFactoryMap.get(fieldName));

                } else if (type.equals("weighted-multi")) {
                    facetHandler = buildWeightedMultiHandler(name, fieldName, termListFactoryMap.get(fieldName), dependSet,
                            invertedIndexPenalty);
                } else if (type.equals("attribute")) {
                    facetHandler = new AttributesFacetHandler(name, fieldName, termListFactoryMap.get(fieldName), null,
                            facetProps);
                } else if (type.equals("histogram")) {
                    // A histogram facet handler is always dynamic
                    RuntimeFacetHandlerFactory<?, ?> runtimeFacetFactory = getHistogramFacetHandlerFactory(facet, name, paramMap);
                    runtimeFacets.add(runtimeFacetFactory);
                    facetInfo.setRunTime(true);
                } else if (type.equals("dynamicTimeRange")) {
                    if (dependSet.isEmpty()) {
                        Assert.isTrue(fieldName != null && fieldName.length() > 0, "Facet handler " + name + " requires either depends or column attributes");
                        RangeFacetHandler internalFacet = new RangeFacetHandler(name + "_internal", fieldName, new PredefinedTermListFactory(Long.class, DynamicTimeRangeFacetHandler.NUMBER_FORMAT), null);
                        facets.add(internalFacet);
                        dependSet.add(internalFacet.getName());
                    }
                    RuntimeFacetHandlerFactory<?, ?> runtimeFacetFactory = getDynamicTimeFacetHandlerFactory(name, fieldName, dependSet, paramMap);
                    runtimeFacets.add(runtimeFacetFactory);
                    facetInfo.setRunTime(true);

                } else if (type.equals("custom")) {
                    boolean isDynamic = facet.optBoolean("dynamic");
                    // Load from custom-facets spring configuration.
                    if (isDynamic) {
                        RuntimeFacetHandlerFactory<?, ?> runtimeFacetFactory = pluginRegistry.getRuntimeFacet(name);
                        runtimeFacets.add(runtimeFacetFactory);
                        facetInfo.setRunTime(true);
                    } else {
                        facetHandler = pluginRegistry.getFacet(name);
                    }
                } else {
                    throw new IllegalArgumentException("type not supported: " + type);
                }

                if (facetHandler != null) {
                    facets.add(facetHandler);
                }
            } catch (Exception e) {
                throw new ConfigurationException("Error parsing schema: "
                        + facet, e);
            }
        }

        facets.addAll((Collection<? extends FacetHandler<?>>) pluggableSearchEngineManager.createFacetHandlers());
        // uid facet handler to be added by default
        UIDFacetHandler uidHandler = new UIDFacetHandler(UID_FACET_NAME);
        facets.add(uidHandler);
        sysInfo.setFacetInfos(facetInfos);

        return sysInfo;
    }


    public static RuntimeFacetHandlerFactory<?, ?> getDynamicTimeFacetHandlerFactory(final String name, String fieldName, Set<String> dependSet,
                                                                                     final Map<String, List<String>> paramMap) {

        Assert.isTrue(dependSet.size() == 1, "Facet handler " + name + " should rely only on exactly one another facet handler, but accodring to config the depends set is " + dependSet);
        final String depends = dependSet.iterator().next();
        Assert.notEmpty(paramMap.get("range"), "Facet handler " + name + " should have at least one predefined range");

        return new AbstractRuntimeFacetHandlerFactory<FacetHandlerInitializerParam, RuntimeFacetHandler<?>>() {

            @Override
            public String getName() {
                return name;
            }

            @Override
            public RuntimeFacetHandler<?> get(FacetHandlerInitializerParam params) {
                long overrideNow = -1;
                try {
                    String overrideProp = System.getProperty("override.now");
                    if (overrideProp != null) {
                        overrideNow = Long.parseLong(overrideProp);
                    }
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }

                long now = System.currentTimeMillis();
                if (overrideNow > 0)
                    now = overrideNow;
                else {
                    if (params != null) {
                        long[] longParam = params.getLongParam("now");
                        if (longParam == null || longParam.length == 0)
                            longParam = params.getLongParam("time")// time will also work

                        if (longParam != null && longParam.length > 0)
                            now = longParam[0];
                    }
                }

                List<String> ranges = paramMap.get("range");

                try {
                    return new DynamicTimeRangeFacetHandler(name, depends, now, ranges);
                } catch (ParseException ex) {
                    throw new RuntimeException(ex);
                }
            }
        };
    }
}
TOP

Related Classes of com.senseidb.conf.SenseiFacetHandlerBuilder

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.