com.tuplejump.stargate.lucene.query.function.AggregateFunction.java Source code

Java tutorial

Introduction

Here is the source code for com.tuplejump.stargate.lucene.query.function.AggregateFunction.java

Source

/*
 * Copyright 2014, Tuplejump Inc.
 *
 * Licensed 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 com.tuplejump.stargate.lucene.query.function;

import com.tuplejump.stargate.RowIndex;
import com.tuplejump.stargate.Utils;
import com.tuplejump.stargate.cassandra.ResultMapper;
import com.tuplejump.stargate.cassandra.RowFetcher;
import com.tuplejump.stargate.cassandra.SearchSupport;
import com.tuplejump.stargate.lucene.*;
import com.tuplejump.stargate.lucene.Properties;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Row;
import org.codehaus.jackson.annotate.JsonProperty;
import org.mvel2.MVEL;
import org.mvel2.ParserConfiguration;
import org.mvel2.ParserContext;
import org.mvel2.compiler.ExecutableStatement;

import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.*;

/**
 * User: satya
 */

public class AggregateFunction implements Function {

    protected AggregateFactory[] aggregates;
    protected String[] groupBy;
    List<String> groupByFields;
    protected boolean distinct = false;
    Map<String, Integer> positions;
    String[] aggregateFields;
    int chunkSize = 1;
    ExecutableStatement[] groupByExpressions;
    String[] imports;
    boolean noScript;
    boolean[] simpleExpressions;
    Options options;
    Group group;
    protected String[] selection;

    public AggregateFunction(@JsonProperty("aggregates") AggregateFactory[] aggregates,
            @JsonProperty("distinct") boolean distinct, @JsonProperty("groupBy") String[] groupBy,
            @JsonProperty("chunkSize") Integer chunkSize, @JsonProperty("imports") String[] imports,
            @JsonProperty("noScript") boolean noScript) {
        this.aggregates = aggregates;
        this.distinct = distinct;
        this.groupBy = groupBy;
        this.imports = imports;
        if (chunkSize != null)
            this.chunkSize = chunkSize;
        this.noScript = noScript;
    }

    @Override
    public boolean shouldTryScoring() {
        return false;
    }

    public Map<String, Integer> getPositions() {
        return positions;
    }

    public boolean[] getSimpleExpressions() {
        return simpleExpressions;
    }

    @Override
    public List<Row> process(ResultMapper resultMapper, ColumnFamilyStore table, RowIndex currentIndex)
            throws Exception {
        Options options = resultMapper.searchSupport.getOptions();
        if (aggregates.length == 1 && !aggregates[0].distinct && "count".equalsIgnoreCase(aggregates[0].getType())
                && groupBy == null) {
            //this means it is a count-star. we can simply return the size of the index results
            Count count = new Count(aggregates[0], false);
            count.count = resultMapper.collector.docs().size();
            group.groups.put(new Tuple(options.nestedFields, Collections.EMPTY_MAP, simpleExpressions), count);
            Row row = resultMapper.tableMapper.getRowWithMetaColumn(group.toByteBuffer());
            return Collections.singletonList(row);
        }
        Tuple tuple = createTuple(options);
        if (resultMapper.collector.canByPassRowFetch()) {
            for (IndexEntryCollector.IndexEntry indexEntry : resultMapper.collector.docs()) {
                load(tuple, indexEntry);
                group.addTuple(tuple);
            }
        } else {
            RowFetcher rowFetcher = new RowFetcher(resultMapper);
            for (Row row : rowFetcher.fetchRows()) {
                resultMapper.tableMapper.load(positions, tuple, row);
                group.addTuple(tuple);
            }

        }
        Utils.SimpleTimer timer3 = Utils.getStartedTimer(SearchSupport.logger);
        ByteBuffer groupBuffer = group.toByteBuffer();
        timer3.endLogTime("Aggregation serialization  [" + group.groups.size() + "] results");

        Row row = resultMapper.tableMapper.getRowWithMetaColumn(groupBuffer);
        return Collections.singletonList(row);
    }

    public Tuple createTuple(Options options) {
        return new Tuple(options.nestedFields, positions, simpleExpressions);
    }

    @Override
    public void init(Options options) {
        this.options = options;
        int k = 0;
        positions = new HashMap<>();
        aggregateFields = new String[aggregates.length];
        for (AggregateFactory aggregateFactory : aggregates) {
            String field = aggregateFactory.getField();
            if (field != null) {
                aggregateFields[k] = field;
                positions.put(field, k++);
            }
        }

        ParserConfiguration parserConfig = getParserConfiguration();

        if (groupBy != null) {
            this.groupByExpressions = new ExecutableStatement[groupBy.length];
            this.simpleExpressions = new boolean[groupBy.length];
            groupByFields = new ArrayList<>();
            for (int i = 0; i < groupBy.length; i++) {
                String groupByField = groupBy[i];
                String groupByCol = getGroupBy(groupByField);
                boolean isSimpleExpression = options.types.containsKey(getColumnName(groupByCol));
                ParserContext parserContext = new ParserContext(parserConfig);
                groupByExpressions[i] = (ExecutableStatement) MVEL.compileExpression(groupByField, parserContext);
                if (isSimpleExpression) {
                    simpleExpressions[i] = true;
                    int pos = k++;
                    positions.put(groupByCol, pos);
                    groupByFields.add(groupByCol);
                } else {
                    simpleExpressions[i] = false;
                    Set<String> keys = parserContext.getInputs().keySet();
                    for (String key : keys) {
                        int pos = k++;
                        boolean canResolve = options.types.containsKey(getColumnName(key));
                        if (canResolve) {
                            String groupByColField = getGroupBy(key);
                            positions.put(key, pos);
                            groupByFields.add(groupByColField);
                        }
                    }
                }
            }

        }
        group = new Group(options, aggregates, groupBy, groupByExpressions);

    }

    private ParserConfiguration getParserConfiguration() {
        ParserConfiguration parserConfig = new ParserConfiguration();
        parserConfig.addPackageImport("java.util");
        parserConfig.addPackageImport("org.apache.commons.lang3");
        parserConfig.addPackageImport("org.joda.time");
        parserConfig.addImport(Math.class);
        if (imports != null) {
            for (String imported : imports)
                parserConfig.addPackageImport(imported);
        }
        return parserConfig;
    }

    public List<String> getGroupByFields() {
        return groupByFields;
    }

    public List<String> getAggregateFields() {
        if (aggregateFields == null)
            return null;
        List<String> aggFields = new ArrayList<>(aggregateFields.length);
        for (String field : aggregateFields) {
            if (field != null)
                aggFields.add(field);
        }
        return aggFields;
    }

    public static Type getLuceneType(Options options, String field) {
        String validatorFieldName = getColumnName(field);
        if (validatorFieldName == null)
            return null;
        return options.types.get(validatorFieldName);
    }

    public static String getColumnName(String field) {
        return field != null ? Constants.dotSplitter.split(field).iterator().next() : null;
    }

    public static String getGroupBy(String groupBy) {
        return groupBy != null ? groupBy.toLowerCase() : null;
    }

    public static class NumberComparator implements Comparator<Number> {

        public int compare(Number a, Number b) {
            return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
        }

        public static int compareNumbers(Number a, Number b) {
            return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
        }

    }

    public void load(Tuple tuple, IndexEntryCollector.IndexEntry entry) {
        for (String field : positions.keySet()) {
            Type validator = options.types.get(field);
            load(tuple, entry, field, validator);
            if (validator == null) {
                Iterator<String> fieldNameParts = Constants.dotSplitter.split(field).iterator();
                String columnName = fieldNameParts.next();
                if (options.nestedFields.contains(columnName)) {
                    Properties columnProps = options.fields.get(columnName);
                    Properties fieldProps;
                    if (columnProps.getType() == Type.map) {
                        fieldProps = columnProps.getFields().get("_value");
                    } else {
                        fieldProps = columnProps.getFields().get(fieldNameParts.next());
                    }
                    load(tuple, entry, field, fieldProps.getType());
                }
            }
        }
    }

    private void load(Tuple tuple, IndexEntryCollector.IndexEntry entry, String field, Type validator) {
        if (validator != null) {
            if (validator.isNumeric()) {
                tuple.tuple[this.positions.get(field)] = entry.getNumber(field);
            } else if (validator == Type.date) {
                Number number = entry.getNumber(field);
                tuple.tuple[this.positions.get(field)] = new Date(number.longValue());
            } else {
                tuple.tuple[this.positions.get(field)] = entry.getString(field);
            }
        }
    }

    public Group getGroup() {
        return group;
    }
}