protonemedia / laravel-cross-eloquent-search

Laravel package to search through multiple Eloquent models. Supports sorting, pagination, scoped queries, eager load relationships and searching through single or multiple columns.
https://protone.media/blog/search-through-multiple-eloquent-models-with-our-latest-laravel-package
MIT License
1.1k stars 80 forks source link

addMany returns inconsitent results #47

Closed vwasteels closed 2 years ago

vwasteels commented 2 years ago

Hello,

I have an issue : In the example below there is 100 Project and 0 Formation in DB, so /testA and /testB should return the exact same thing :

    Route::get('/testA', function (Request $request) {
        return Search::addMany([
            [Project::query(), 'title'],
        ])->get('');
    });

    Route::get('/testB', function (Request $request) {
        return Search::addMany([
            [Project::query(), 'title'],
            [Formation::query(), 'title'],
        ])->get('');
    });

But it is not the case, I don't get the results in the same order in the 2 queries, event if I add ->orderByRelevance()

Any idea why is this ? Thank you

vwasteels commented 2 years ago

Hello, I still have this issue, please just let me know if you plan looking on this or if I need to move on to some other solution, Thanks anyway for the great component :)

pascalbaljet commented 2 years ago

@bradyrenting added a test to confirm the library works correctly: https://github.com/protonemedia/laravel-cross-eloquent-search/commit/01b0148df7b8c70f59bb99a908a79f51d88a1be5

Maybe the inconsistency comes from ordering a column with a lot of equal values?

vwasteels commented 2 years ago

thanks for your answer , I use orderByRelevance, even when passing an empty string to the get methods , I'll try using updated_at instead in that case.

vwasteels commented 2 years ago

It's working when forcing order by titre, thanks !

I put the code below if anyone wants a complexe example of implementation :)

    Route::get('/search', function (Request $request) {

        $types = [
            'projet' => [
                'query' => Projet::query(),
                'className' => '\App\Models\Projet'
            ],
            'formation' => [
                'query' => Formation::query(),
                'className' => '\App\Models\Formation'
            ],
            'article' => [
                'query' => Article::query(),
                'className' => '\App\Models\Articles\Article'
            ],
        ];

        // filter types (no types = all types)
        $queriedTypes = array_filter(explode(',', $request->get('types')));
        if (!empty($queriedTypes)) {
            foreach ($types as $key => $type) {
                if (!in_array($key, $queriedTypes)) {
                    unset($types[$key]);
                }
            }
        }

        // filter taxonomies
        $taxonomies = [
            'thematiques',
            'leviers',
            'tags'
        ];
        foreach ($taxonomies as $taxonomy) {
            $queriedTaxIds = array_filter(explode(',', $request->get($taxonomy)));
            if (!empty($queriedTaxIds)) {
                foreach ($types as $type) {
                    $type['query']->whereHas(
                        $taxonomy, 
                        function (Builder $query) use ($queriedTaxIds) {
                            $query->whereIn('id', $queriedTaxIds);
                        }
                    );
                }
            }
        }

        // filter articleTypes
        if (array_key_exists('article', $types)) {
            $queriedArticleTypesIds = array_filter(explode(',', $request->get('articleTypes')));
            if ($queriedArticleTypesIds) {
                $articleableTypesSlugs = ArticleableType::findMany($queriedArticleTypesIds)->pluck('slug');
                $types['article']['query']->whereIn('articleable_type', $articleableTypesSlugs);
            }
        }

        // per page pagination
        $perPage = $request->get('per_page') ? (int)$request->get('per_page') : 10;

        // prepare for launch...
        $textQuery = $request->get('q');
        $models = [];
        foreach ($types as $type) {
            $model = [$type['query'], $type['className']::$searchable];

            // no text search : orderBy titre
            if (!$textQuery) $model[] = 'titre';

            $models[] = $model;
        }

        // and launching !
        $search = Search::addMany($models)
            ->beginWithWildcard()
            ->endWithWildcard()
            ->paginate($perPage)
            ->includeModelType();

        // change orderBy according to text query
        if($textQuery) {
            $search->orderByRelevance();
        }

        $results = $search->get($request->get('q'));

        return ContentResource::collection($results);
    });