Skip to content
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

Searching multiple types/models #29

Open
paulocoghi opened this issue May 20, 2015 · 10 comments
Open

Searching multiple types/models #29

paulocoghi opened this issue May 20, 2015 · 10 comments

Comments

@paulocoghi
Copy link

Is it possible to search multiple types/models at the same time with Elasticquent?

@alfchee
Copy link

alfchee commented Jun 17, 2015

I'm very interested in the answer too, or functionality

@ilikourou
Copy link

I would also like to know if this is possible. Thanks.

@timgws
Copy link

timgws commented Jun 23, 2015

Presently it's not possible in Elastiquent, it is however possible using ElasticSearch:

By not limiting our search to a particular index or type, we have searched across all documents in the cluster. Elasticsearch forwarded the search request in parallel to a primary or replica of every shard in the cluster, gathered the results to select the overall top 10, and returned them to us.

Changing Elasticquent to search multiple models at the same time will require a bit of modification (and also from the source level would not make sense, currently).

https://github.com/adamfairholm/Elasticquent/blob/master/src/ElasticquentTrait.php#L244-L269

    public static function searchByQuery($query = null, $aggregations = null, $sourceFields = null, $limit = null, $offset = null, $sort = null)
    {
        $instance = new static;
        $params = $instance->getBasicEsParams(true, true, true, $limit, $offset);
        if ($sourceFields) {
            $params['body']['_source']['include'] = $sourceFields;
        }
        if ($query) {
            $params['body']['query'] = $query;
        }
        if ($aggregations) {
            $params['body']['aggs'] = $aggregations;
        }

        if ($sort) {
            $params['body']['sort'] = $sort;
        }
        $result = $instance->getElasticSearchClient()->search($params);
        return new ResultCollection($result, $instance = new static);
    }

The problem here is that ResultCollection($result, $instance = new static); essentially says that the ElasticquentResultCollection item that is returned will be based on the model that you initiate the search on.

Searching multiple indexes at a time will require a new class that performs just a search, and somehow the collection that is returned should be able to differentiate between different models.

Adding this would make the code more complex, and I can't see too many benefits due to adding it.

@ilikourou
Copy link

Usually you don't build a Laravel app based only on one model. What happens if you have to search the whole site for something, is there any other way to do it?

I tried making two different searches (on different types) and then use the merge() method to merge the returned collections, but it didn't work.

@alfchee
Copy link

alfchee commented Jun 23, 2015

I've tried to use merge() too, and doesn't work because is not the same method of \Illuminate\Support\Collection , what I have to do to merge the results was something like this:

   $result = new \Illuminate\Support\Collection([]);

    //query
    $models1 = FirstModel::search($query);
    $models2 = SecondModel::search($query);

    // work the collection
    foreach($models1 as $model1) {
        $result->push($model1);
    }
    foreach($models2 as $model2) {
        $result->push($model2);
    }

@timgws
Copy link

timgws commented Jun 24, 2015

OK, I will look into why the merging is not working a little later tonight, then. It definitely should be OK.

@timgws
Copy link

timgws commented Jun 24, 2015

Edit: I said I would look into why the merging is not working a little later tonight, but a quick check shows why merge will not work.

ResultCollection extends Illuminate\Database\Eloquent\Collection.

The merge function creates a new instance of the class you are calling merge on.

@alfchee this will mean in your case that when you are attempting to merge SecondModel into FirstModel, all of the values from SecondModel are being merged into a new FirstModel instance (which is usually not what you want to do).

This brings me back to my original comment:

Changing Elasticquent to search multiple models at the same time will require a bit of modification (and also from the source level would not make sense, currently).

The best way to do this might be to create a new Facade (called Search?) that allows you to perform multiple searches based on the models that you want to search on, then merging them similar to what @alfchee has shown above.

@chongkan
Copy link

chongkan commented Nov 6, 2015

+1 for alfchee's method.

I needed to show results from different tables: Articles, Blog entries, Deals, etc. And I needed to display them in order of relevance score, regardless of the type of document.

For sorting based on the documentScore:

$result->sortByDesc(function ($item){
return $item->documentScore();
});

@taledo
Copy link

taledo commented Mar 10, 2016

I too would find a nice solution very valuable, as like some said earlier, many Laravel apps are built on many models, with relationships etc.

Anyhow, I got around this using alfchee's method above. It's a little hacky, but it get's my results and I've also grouped them so I can later iterate and "break up" my search results by type etc...

$books = Book::search($query);
$articles = Article::search($query);

$all = array_merge(
  array(
    'books' => $books->all(),
    'articles' => $articles->all()
  )
);

//Can now iterate on this in my view etc..
return $all;

@aniltekinarslan
Copy link

I wrote basic && useful function. You can edit search filters or query in if blocks.

    public function searchMultipleTypes($tableNames = [], $queryText = '')
    {
        if(!$tableNames || $queryText == '')
            return [];

        $all = [];

        foreach ($tableNames as $table)
        {
            if($table == (new Category())->getTable())
            {
                $params['body']['query']['match']['title'] = $queryText;
                $all[$table] = Category::complexSearch($params);
            }

            else if($table == (new Post())->getTable())
                $all[$table] = Post::search($queryText);

            else if($table == (new City())->getTable())
                $all[$table] = City::search($queryText);
        }

        return $all;
    }

and you can call with:

$searchTables = ['categories', 'posts', 'cities'];
        $data = searchMultipleTypes($searchTables, $queryText);

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

7 participants