Skip to content

Filter implementation for ElasticSearch #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
nilportugues opened this issue Aug 1, 2017 · 0 comments
Open

Filter implementation for ElasticSearch #1

nilportugues opened this issue Aug 1, 2017 · 0 comments

Comments

@nilportugues
Copy link
Member

class FilterToElasticSearchQuery
{
    const MUST_NOT = 'must_not';
    const MUST = 'must';
    const SHOULD = 'should';

    /**
     * @param FilterInterface $filter
     * @param array $mapping
     * @return array
     */
    public static function filter(FilterInterface $filter, array $mapping): array
    {
        $conditions = [];

        foreach ($filter->filters() as $condition => $filters) {
            $filters = self::removeEmptyFilters($filters);
            if (count($filters) > 0) {
                self::processConditions($mapping, $conditions, $condition, $filters);
            }
        }

        return $conditions;
    }

    /**
     * @param array $filters
     *
     * @return array
     */
    private static function removeEmptyFilters(array $filters)
    {
        $filters = array_filter(
            $filters,
            function ($v) {
                return count($v) > 0;
            }
        );

        return $filters;
    }

    /**
     * @param array $mapping
     * @param array $conditions
     * @param $condition
     * @param array $filters
     */
    private static function processConditions(
        array &$mapping,
        array &$conditions,
        $condition,
        array &$filters
    ) {
        switch ($condition) {
            case self::MUST:
                self::apply($mapping, $conditions, $filters, 'must');
                break;

            case self::MUST_NOT:
                self::apply($mapping, $conditions, $filters, 'must_not');
                break;

            case self::SHOULD:
                self::apply($mapping, $conditions, $filters, 'should');
                break;
        }
    }

    /**
     * @param array $mapping
     * @param array $conditions
     * @param array $filters
     * @param $operator
     */
    protected static function apply(
        array &$mapping,
        array &$conditions,
        array $filters,
        $operator
    ) {
        foreach ($filters as $filterName => $valuePair) {
            foreach ($valuePair as $key => $value) {
                if (false === array_key_exists($key, $mapping)) {
                    break;
                }

                $key = self::fetchColumnName($mapping, $key);
                if (is_array($value) && count($value) > 0) {
                    $value = array_values($value);
                    if (count($value[0]) > 1) {
                        switch ($filterName) {
                            case BaseFilter::RANGES:
                                self::rangeQuery($conditions, $operator, $key, $value);
                                break;

                            case BaseFilter::NOT_RANGES:
                                $operator = self::getOppositeOperator($operator);

                                self::rangeQuery($conditions, $operator, $key, $value);
                                break;
                        }
                    } else {
                        switch ($filterName) {
                            case BaseFilter::GROUP:
                                $operatorIn = 'should';
                                if (self::MUST_NOT === $operator) {
                                    $operatorIn = 'should_not';
                                }

                                self::inGroupQuery($conditions, $operatorIn, $key, $value);
                                break;
                            case BaseFilter::NOT_GROUP:
                                $operatorIn = 'should_not';
                                if (self::MUST_NOT === self::getOppositeOperator($operator)) {
                                    $operatorIn = 'should';
                                }

                                self::inGroupQuery($conditions, $operatorIn, $key, $value);
                                break;
                        }
                    }
                }

                $value = (array)$value;
                $value = array_shift($value);

                switch ($filterName) {
                    case BaseFilter::GREATER_THAN_OR_EQUAL:
                        self::query($conditions, $operator, $key, 'gte', $value);
                        break;

                    case BaseFilter::GREATER_THAN:
                        self::query($conditions, $operator, $key, 'gt', $value);
                        break;

                    case BaseFilter::LESS_THAN_OR_EQUAL:
                        self::query($conditions, $operator, $key, 'lte', $value);
                        break;

                    case BaseFilter::LESS_THAN:
                        self::query($conditions, $operator, $key, 'lt', $value);
                        break;

                    case BaseFilter::CONTAINS:
                        self::query($conditions, $operator, $key, 'match', '*' . $value . '*');
                        break;

                    case BaseFilter::NOT_CONTAINS:
                        $operator = self::getOppositeOperator($operator);

                        self::query($conditions, $operator, $key, 'match', '*' . $value . '*');
                        break;

                    case BaseFilter::EQUALS:
                        self::query($conditions, $operator, $key, 'match', $value);
                        break;

                    case BaseFilter::NOT_EQUAL:
                        $operator = self::getOppositeOperator($operator);

                        self::query($conditions, $operator, $key, 'match', $value);
                        break;

                    case BaseFilter::EMPTY_FILTER:
                        self::query($conditions, $operator, $key, 'match', null);
                        break;

                    case BaseFilter::NOT_EMPTY:
                        $operator = self::getOppositeOperator($operator);
                        self::query($conditions, $operator, $key, 'match', null);
                        break;

                    case BaseFilter::ENDS_WITH:
                        $newValue = '*' . $value;
                        self::query($conditions, $operator, $key, 'match', $newValue);
                        break;

                    case BaseFilter::NOT_ENDS:
                        $operator = self::getOppositeOperator($operator);
                        self::query($conditions, $operator, $key, 'match', '*' . $value);
                        break;

                    case BaseFilter::STARTS_WITH:
                        self::query($conditions, $operator, $key, 'match', '*' . $value);
                        break;

                    case BaseFilter::NOT_STARTS:
                        $operator = self::getOppositeOperator($operator);
                        self::query($conditions, $operator, $key, 'match', $value . '*');
                        break;
                }
            }
        }
    }

    /**
     * @param $columns
     * @param $propertyName
     *
     * @return int
     */
    protected static function fetchColumnName(array &$columns, $propertyName)
    {
        if (empty($columns[$propertyName])) {
            throw new \RuntimeException(sprintf('Property %s has no associated column.', $propertyName));
        }

        return $columns[$propertyName];
    }

    /**
     * @param array $conditions
     * @param $operator
     * @param $key
     * @param $value
     */
    protected static function rangeQuery(array &$conditions, $operator, $key, $value)
    {
        $rangeCondition = ['gte' => $value[0][0], 'lte' => $value[0][1]];
        if (self::MUST_NOT === $operator) {
            $rangeCondition = ['lt' => $value[0][0], 'gt' => $value[0][1]];
        }

        $conditions['query']['bool']['filter'][] = ['range' => [$key => $rangeCondition]];
        $conditions['query']['bool']['filter'] = array_values($conditions['query']['bool']['filter']);
    }

    /**
     * @param $operator
     * @return string
     */
    protected static function getOppositeOperator($operator): string
    {
        if ($operator === self::MUST) {
            $operator = self::MUST_NOT;
        }

        if ($operator === self::MUST_NOT) {
            $operator = self::MUST;
            return $operator;
        }
        return $operator;
    }

    /**
     * @param array $conditions
     * @param $operator
     * @param $key
     * @param $value
     */
    protected static function inGroupQuery(array &$conditions, $operator, $key, $value)
    {
        $query =  [
            ['bool' => [$operator => ['exists' => ['field' => $key]]]]
        ];

        foreach ($value as $v) {
            $query[] = ['match' => [$key => $v]];
        }

        $conditions['query']['bool']['filter']['bool'][$operator][] = $query;
    }

    /**
     * @param array $conditions
     * @param $operator
     * @param $key
     * @param $op
     * @param $value
     */
    protected static function query(array &$conditions, $operator, $key, $op, $value)
    {
        $conditions['query']['bool'][$operator][] = [$op => [$key => $value]];
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant