Giter Club home page Giter Club logo

filtermanagerbundle's Introduction

ONGR FilterManagerBundle

Filter manager is used for listing documents. It provides ties between commonly used filtering options and UI elements with Elasticsearch repositories. It is important to mention that filtering is everything what has impact on list, it can be:

  • Filtering on specific field value object have (color, country etc.)
  • Filtering range (price range, distance from point etc.)
  • Documents list pagination. Paging changes representation of list, so it is considered to be filter and is treated like one.
  • Documents list sorting. Same as paging - sorting is filter in this bundle.
  • Any custom factor which has influence (not always directly visible) on result list. It can exclude, boost, modify some results, collect some metrics or any other action you can imagine.

If you need any help, stack overflow is the preffered and recommended way to ask ONGR support questions.

Build Status Coverage Status Latest Stable Version Scrutinizer Code Quality

Documentation

For online documentation of the bundle click here. All docs pages are located in Resources/doc/.

Installation

Step 1: Install FilterManager bundle

FilterManager bundle is installed using Composer.

# You can require any version you need, check the latest stable to make sure you are using the newest version.
$ composer require ongr/filter-manager-bundle "~3.0"

Please note that filter manager requires Elasticsearch bundle, guide on how to install and configure it can be found here.

Step 2: Enable FilterManager bundle

Enable Filter Manager bundle in your AppKernel:

// config/bundles.php

<?php

return [
    ...

    ONGR\ElasticsearchBundle\ONGRElasticsearchBundle::class => ['all' => true],
    ONGR\FilterManagerBundle\ONGRFilterManagerBundle::class => ['all' => true],
    JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],

    ...
];

Step 3: Add configuration for manager

Add minimal configuration for Elasticsearch and FilterManager bundles.

# app/config/config.yml

ongr_elasticsearch:
  indexes:
    App\Document\Product:
      hosts: 
         - 127.0.0.1:9200 

ongr_filter_manager:
    managers:
        search_list: # <- Filter manager name
            filters:
                - country
            repository: App\Document\Product # <- Product document rindex service (used to be a repository prior to v3.0)
    filters:
        country: # <- Filter name
            type: choice
            request_field: country
            document_field: country

Note that Product document has to be defined. More about that in ElasticsearchBundle documentation.

In this particular example, we defined a single filter manager named search_list to filter documents from product repository, and we will be using the filter named country to filter on countries defined in document.

Step 4: Use your new bundle

FilterManagerBundle is ready to use. When you define filter manager the bundle generates a service according manager name. In this particular case it will be ongr_filter_manager.manager.search_list.

To get a list grab the service and call handleRequest(). Here's a short example in the controller:

<?php
 
use ONGR\FilterManagerBundle\DependencyInjection\ONGRFilterManagerExtension;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
 
class ProductController extends Controller
{
    /**
     * @param Request $request Request.
     *
     * @return Response
     */
    public function listAction(Request $request)
    {
        $searchList = $this->get(ONGRFilterManagerExtension::getFilterManagerId('search_list'))
                          ->handleRequest($request);
        $this->render(
            'AppBundle:Product:list.html.twig',
            [
              'filters' => $searchList->getFilters(),  
              'products' => $searchList->getResult(),  
            ]
        );                  
    }
}

More information how to use filters and render the results are in basics topic here.

Troubleshooting

If you face any issue or difficulty by implementing bundle, do not be afraid to create an issue with bug or question. Also ONGR organization has a tag in Stackoverflow so you can ask about all ONGR bundles also there.

License

This bundle is covered by the MIT license. Please see the complete license in the bundle LICENSE file.

filtermanagerbundle's People

Contributors

arturlitvinavicius avatar asev avatar dvondrak avatar einorler avatar franckbrignoli avatar grandltu avatar kazysgurskas avatar koktaildotcom avatar linasmo avatar mvar avatar norkunas avatar rolandaszelionka avatar saimaz avatar tautrimas avatar tomaspocevicius avatar trandangtri avatar trylika avatar zylius avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

filtermanagerbundle's Issues

We should not force users to configure at least one manager and filter

f.e. If we want to use filter manager in other components we define our filter manager without configuration. When user would use a component (which needs filter manager to be added in bundles array) FMB requires minimal configuration with one filter and manager, which would be left unused.

MultiTermChoice with NOT AND

Similar to ticket #105, we also need NOT AND filter. This is very useful in backend when one wants to exclude some information.

Our use case: select general tag PLANTS, and exclude DEAD_PLANTS leaving only alive ones. This widget greatly reduces the need to create tags and categories for every possible variation of combinations.

Our code:

/**
 * Extends MultiTermChoiceAnd with 'not' functionality.
 */
class MultiTermChoiceAndNot extends MultiTermChoiceAnd
{
    /** @var TermsFilter */
    protected $filterNot;

    /**
     * {@inheritdoc}
     */
    public function modifySearch(Search $search, FilterState $state = null, SearchRequest $request = null)
    {
        $this->filter = new TermsFilter($this->getField(), $state->getValue());
        $this->filterNot = new NotFilter($this->filter);
        $this->filterValues = $state->getValue();

        if ($state && $state->isActive()) {
            $search->addPostFilter($this->filterNot);
        }
    }
}

Please note that it's broken in a sense, that when adding new NOT terms, counts for new entries are a bit off in the widget. This is very subtle and happens, if newly excluded entries shares the same terms with already excluded entries.

Exception with ElasticsearchBundle dev-master version in SingleTermChoice

In code:

if ($relatedSearch->getPostFilters() && $relatedSearch->getPostFilters()->isRelevant()) {

Exception is thrown:

Attempted to call method "isRelevant" on class "ONGR\ElasticsearchBundle\DSL\Filter\TermFilter".
500 Internal Server Error - UndefinedMethodException

Exception is thrown, when we upgraded to ESB dev-master in our application.

There is a need for a filter which can handle object grouping

Basically we need to support products with variants. The first question is how the data is indexed. If the variation information is provided inside some field, then there is nothing to do, this is simple choice filter. Otherwise if each variant stands as single document we have to think about grouping filter.

Investigate if there is possible to create filter list cache

Filter manager builds filter list from configuration. In this process there are so many recursions and blocks rebuilding operations, just to make sure build correctly relations on selected filters.

What if we could cache all these builds and set only values to already builded list.

"agg_range" rewrite itself

If you use 2 range filter on one page first filter rewrite second filter

Resolve:

    /**
     * {@inheritdoc}
     */
    public function preProcessSearch(Search $search, Search $relatedSearch, FilterState $state = null)
    {
        $stateAgg = new StatsAggregation('range_agg'. $this->getField());
        $stateAgg->setField($this->getField());
        $search->addAggregation($stateAgg);
    }

    /**
     * {@inheritdoc}
     */
    public function getViewData(DocumentIterator $result, ViewData $data)
    {
        /** @var $data ViewData\RangeAwareViewData */

        $data->setMinBounds($result->getAggregations()['range_agg' . $this->getField()]->getValue()['min']);
        $data->setMaxBounds($result->getAggregations()['range_agg' . $this->getField()]->getValue()['max']);

        return $data;
    }

Do not require to add filters in the filter manager

Maybe would be good to remove requirement for adding filters in filter manager config and in the filter definitions node. What if user just starting to work and doesn't know yet how to work with this and want to debug a bit.

It won't change anything, but will add more flexibility.

update docs

ongr/pager-bundle is deprecated and now pager is part of filter manager.

FunctionScore filter

Function score filter for boosting is missing. I'm going to implement it.
Any ideas or suggestions are welcome.

Range filter throws Exception when malformed parameters are passed

Range filter widget throws Exception when passed range arguments are malformed.

public function getState(Request $request)
    {
        $state = parent::getState($request);

        if (!$state->isActive()) {
            return $state;
        }

        $values = explode(';', $state->getValue(), 2);

        if (count($values) < 2) {
            throw new \UnderflowException(
                "Range request field value must contain from, to values delimited by ';', got {$state->getValue()}."
            );
        }

        $normalized['gt'] = (int)$values[0];
        $normalized['lt'] = (int)$values[1];

        $state->setValue($normalized);

        return $state;
    }

Maybe would be better to set filter as inactive and execute request without it than throw exception.

if (count($values) < 2) {
        $state->setActive(false);

        return $state;
    }

MultiTermChoice with AND instead of or

There is a ticket #63, but it was converted to minimum_should_match ticket. We really need AND support. We contribute our implementation.

Notice duplication involved by not being override methods easily. Additional code is needed to allow showing correct counts when adding more terms to active multi-choice widget.

/**
 * Overrides original MultiTermChoice behaviour and uses 'and' filter instead of default 'or'.
 */
class MultiTermChoiceAnd extends MultiTermChoice
{
    /** @var TermsFilter */
    protected $filter;

    /** @var array */
    protected $filterValues;

    /**
     * {@inheritdoc}
     */
    public function modifySearch(Search $search, FilterState $state = null, SearchRequest $request = null)
    {
        $this->filter = new TermsFilter($this->getField(), $state->getValue(), ['execution' => 'and']);
        $this->filterValues = $state->getValue();

        if ($state && $state->isActive()) {
            $search->addPostFilter($this->filter);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function preProcessSearch(Search $search, Search $relatedSearch, FilterState $state = null)
    {
        $name = $state ? $state->getName() : $this->getField();
        $aggregation = new TermsAggregation($name);
        $aggregation->setField($this->getField());

        if ($this->getSortType()) {
            $aggregation->setOrder($this->getSortType()['type'], $this->getSortType()['order']);
        }

        $aggregation->setSize(0);
        if ($this->getSize() > 0) {
            $aggregation->setSize($this->getSize());
        }

        if ($search->getPostFilters() && !$relatedSearch->getPostFilters()) {
            $relatedSearch = $search;
        }

        if ($relatedSearch->getPostFilters()) {
            $filterAggregation = new FilterAggregation($name . '-filter');

            // Should add itself to aggregation so items counts would be set correctly.
            if ($this->filterValues !== null) {
                $relatedSearch->addPostFilter($this->filter);
            }
            $filterAggregation->setFilter($relatedSearch->getPostFilters());

            $filterAggregation->addAggregation($aggregation);
            $search->addAggregation($filterAggregation);
        } else {
            $search->addAggregation($aggregation);
        }
    }
}

Refactor custom filters configuration

Current configuration for custom filters is too complicated. To use single custom filter with multiple filter managers user must add multiple tags (single tag for each manager).

We should leave single tag here with "filter name" attribute and create alias ongr_filter_manager.FILTER_NAME, so custom filter could be used in configuration like any others.

Another idea: we could even provide a way to extend configuration tree for custom filter types.

Custom filter does not work

I have tried to register custom filter like:

wdn_search.filters.wdn_match:
    class: WDN\SearchBundle\Filters\WDNMatchSearch
    arguments: ["search", "search"]
    tags:
        - { name: ongr_filter_manager.filter, manager: profile_list, filter_name: wdn_match }
<?php
namespace WDN\SearchBundle\Filters;

use ONGR\FilterManagerBundle\Filters\Widget\Search\MatchSearch;

class WDNMatchSearch extends MatchSearch
{
    public function __construct($requestField, $field)
    {
        $this->setRequestField($requestField);
        $this->setField($field);
    }
}

But then i ran into the problems when setting that filter to work:

ongr_filter_manager:
    managers:
        profile_list:
            filters:
                - wdn_match
                - visible
            repository: 'es.manager.default.profile'
    filters:
        match:
            visible:
                request_field: 'visible'
                field: visible

I get error:

ServiceNotFoundException in CheckExceptionOnInvalidReferenceBehaviorPass.php line 58:
The service "ongr_filter_manager.profile_list" has a dependency on a non-existent service "ongr_filter_manager.filter.wdn_match".

If i don't include the filter in list then i get results, but the filter is not applied (request url /searchtest?visible=true&search=somestring):

int(10)
array(2) {
  ["visibility"]=>
  object(ONGR\FilterManagerBundle\Filters\ViewData)#8209 (4) {
    ["state":"ONGR\FilterManagerBundle\Filters\ViewData":private]=>
    object(ONGR\FilterManagerBundle\Filters\FilterState)#8174 (4) {
      ["active":"ONGR\FilterManagerBundle\Filters\FilterState":private]=>
      bool(true)
      ["value":"ONGR\FilterManagerBundle\Filters\FilterState":private]=>
      string(4) "true"
      ["urlParameters":"ONGR\FilterManagerBundle\Filters\FilterState":private]=>
      array(1) {
        ["visible"]=>
        string(4) "true"
      }
      ["name":"ONGR\FilterManagerBundle\Filters\FilterState":private]=>
      string(10) "visibility"
    }
    ["urlParameters":"ONGR\FilterManagerBundle\Filters\ViewData":private]=>
    array(1) {
      ["visible"]=>
      string(4) "true"
    }
    ["resetUrlParameters":"ONGR\FilterManagerBundle\Filters\ViewData":private]=>
    array(0) {
    }
    ["name":"ONGR\FilterManagerBundle\Filters\ViewData":private]=>
    string(10) "visibility"
  }
  ["wdn_match"]=>
  object(ONGR\FilterManagerBundle\Filters\ViewData)#8215 (4) {
    ["state":"ONGR\FilterManagerBundle\Filters\ViewData":private]=>
    object(ONGR\FilterManagerBundle\Filters\FilterState)#8175 (4) {
      ["active":"ONGR\FilterManagerBundle\Filters\FilterState":private]=>
      bool(false)
      ["value":"ONGR\FilterManagerBundle\Filters\FilterState":private]=>
      NULL
      ["urlParameters":"ONGR\FilterManagerBundle\Filters\FilterState":private]=>
      array(0) {
      }
      ["name":"ONGR\FilterManagerBundle\Filters\FilterState":private]=>
      string(9) "wdn_match"
    }
    ["urlParameters":"ONGR\FilterManagerBundle\Filters\ViewData":private]=>
    array(1) {
      ["visible"]=>
      string(4) "true"
    }
    ["resetUrlParameters":"ONGR\FilterManagerBundle\Filters\ViewData":private]=>
    array(1) {
      ["visible"]=>
      string(4) "true"
    }
    ["name":"ONGR\FilterManagerBundle\Filters\ViewData":private]=>
    string(9) "wdn_match"
  }
}

Route config:

wdn_profile_search:
    pattern: /searchtest
    methods: [GET]
    defaults:
        _controller: WDNSearchBundle:Company:test
        managerName: profile_list

Is there anything i don't understand or it's a bug?

Remove document_field filter

This filter does not look very useful and implies dependency from router bundle.

Remove it after documentation is prepared.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.