How to perform nested queries with Elastica Search

2020-06-07 03:12发布

问题:

I have a recipe entity which have some tags (many to many mapping) and I want to search recipes by tags.

Here's my Recipe entity:

/**
 * @ORM\Entity
 * @ORM\Table(name="recipes")
 * @ORM\HasLifecycleCallbacks
 * @ExclusionPolicy("all")
 */
class Recipe
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     * @Expose
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=150)
     * @Expose
     */
    protected $name;

    ...

    /**
     * @ORM\ManyToMany(targetEntity="RecipesTag", inversedBy="recipes")
     * @ORM\JoinTable(name="bind_recipes_tags",
     *      joinColumns={@ORM\JoinColumn(name="tag_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="recipe_id", referencedColumnName="id")}
     *      )
     */
    private $tags;

Here's my config:

fos_elastica:
    clients:
        default: { host: localhost, port: 9200 }
    serializer:
        callback_class: FOS\ElasticaBundle\Serializer\Callback
        serializer: serializer
    indexes:
        td:
            client: default
            types:
                Recipe:
                    mappings:
                        name: ~
                        ingredients: 
                            type: "nested"
                            properties:
                                name: ~
                        tags:
                            type: "nested"
                            properties:
                                name: ~
                                id : 
                                    type : integer
                        categories:
                            type: "nested"
                            properties:
                                name: ~
                                id : 
                                    type : integer

                    persistence:
                        driver: orm # orm, mongodb, propel are available
                        model:  ck\RecipesBundle\Entity\Recipe
                        repository: ck\RecipesBundle\Entity\RecipesRepository
                        provider: ~
                        listener: ~
                        finder: ~

I then added a custom repository to perform searches:

public function filterFind($searchText)
{
    $query_part = new \Elastica\Query\Bool();

    $nested = new \Elastica\Query\Nested();
    $nested->setQuery(new \Elastica\Query\Term(array('name' => array('value' => $searchText))));
    $nested->setPath('tags');
    $query_part->addShould($nested);

    return $this->find($query_part);
}

Then I search like this :

$repositoryManager = $this->get('fos_elastica.manager.orm');
$repository = $repositoryManager->getRepository('ckRecipesBundle:Recipe');

$recipes = $repository->filterFind('mytag');

But I got no results whatsoever despite the fact there are matching results.

I didn't find any answers on google. Anyone can help me ?

回答1:

Here is response how to do it. For me it works. http://obtao.com/blog/2014/04/elasticsearch-advanced-search-and-nested-objects/

Basically, you need to create query like that:

{
    "query":{
        "filtered":{
            "query":{
                "query_string":{
                    "query":"*saf*"
                }
            },
            "filter":{
                "nested":{
                    "path":"categories",
                    "filter":{
                        "bool":{
                            "must": [{
                                "term":{
                                    "categories.id":1
                                }
                            }]
                        }
                    },
                    "query":{
                        "match":{
                            "id":1
                        }
                    }
                }
            }
        }
    }
} 

For me in PHP it look like:

        $bool = new Filter\Bool();
        $bool->addMust(new Filter\Term(['categories.id' => $category->getId()]));

        $nested = new Filter\Nested();
        $nested->setPath("categories");
        $nested->setFilter($bool);

        $nested->setQuery($categoriesQuery);

        $queryObj = new Query\Filtered($queryObj, $nested);

Filter is Elastica\Filter and Query is Elastica\Query



回答2:

Indeed, you forgot to define the Filtered query.

This should work :

$nested->setQuery(new \Elastica\Query\Term(array('name' => array('value' => $searchText))));
$nested->setPath('tags');
$query_part->addShould($nested);

$query = new \Elastica\Query\Filtered($query_part/*, $filters // in case you had other filters*/);
return $this->find($query);