【问题标题】:How can I make this search query more efficient?我怎样才能使这个搜索查询更有效?
【发布时间】:2014-08-29 18:48:38
【问题描述】:

我有一个有点复杂的搜索查询,我想提高效率(如果可能的话)。

这是该查询的完整代码:

Route::post('api/search/{startRow}', function($startRow)
{
    $category = Category::where('name', '=', Input::get('category'))->first();

    // Initialize query
    $query = Resource::with('alerts', 'alerts.type', 'user', 'category', 'comments', 'comments.comments', 'ratings')
        ->where('duplicate', '=', 0);

    // Limit search results
    if(Input::get('show'))
    {
        $show = Input::get('show');
        switch ($show) {
            case 'verified':
                $query->where('verified', '=', true);
                break;

            case 'unverified':
                $query->where('verified', '=', false);
                break;

            case 'alerted':
                $query->has('alerts');
                break;

            case 'unalerted':
                $query->has('alerts', '=', 0);
                break;

            default:
                // The default will be 'all' (show all results)
                break;
        }
    }

    if($category->name != "everything")
        $query->where('category_id', '=', $category->id);


    // Sort the search results
    if(Input::get('sort_type'))
    {
        $sort_by = Input::get('sort_type');

        switch ($sort_by)
        {
            case 'relevance':
                break;

            case 'name_asc':
                $query->orderBy('name', 'asc');
                break;

            case 'name_desc':
                $query->orderBy('name', 'desc');
                break;

            case 'rating_high':
                $query
                    ->leftJoin('ratings', 'ratings.ratable_id', '=', 'resources.id')
                    ->where('ratings.ratable_type', '=', 'Resource')
                    ->orderBy(DB::raw('avg(ratings.score)'), 'desc')
                    ->orderBy(DB::raw('count(ratings.score)'), 'desc')
                    ->select('resources.*')
                    ->groupBy('resources.id');
                break;

            case 'rating_low':
                $query
                    ->leftJoin('ratings', 'ratings.ratable_id', '=', 'resources.id')
                    ->where('ratings.ratable_type', '=', 'Resource')
                    ->orderBy(DB::raw('avg(ratings.score)'), 'asc')
                    ->orderBy(DB::raw('count(ratings.score)'), 'asc')
                    ->select('resources.*')
                    ->groupBy('resources.id');
                break;

            case 'date_new':
                $query->orderBy('created_at', 'desc');
                break;

            case 'date_old':
                $query->orderBy('created_at', 'asc');
                break;

            default:
                break;
        }
    }

    // Search by keyword(s)
    if(Input::get('keyword'))
    {
        $search = Input::get('keyword');
        $searchTerms = explode(' ', $search);

        $fields = array(
            'resources.description',
            'resources.website',
            'resources.additional_info');

        foreach ($searchTerms as $term)
        {
            $query->where('resources.name', 'LIKE', '%'. $term .'%');

            foreach ($fields as $field)
            {
                $query->orWhere($field, 'LIKE', '%'. $term .'%');
            }
        }
    }


    // Search by tag(s)
    if(Input::get('tags'))
    {
        $tags = Input::get('tags');

        $query
            ->select('resources.*')
            ->join('taggables', 'taggables.taggable_id', '=', 'resources.id')
            ->join('tags', 'taggables.tag_id', '=', 'tags.id')
            ->whereIn('tags.id', $tags)
            ->groupBy('resources.id')
            ->havingRaw('COUNT(resources.id)=?', array(count($tags)));
    }

    // Total number of results
    $count = $query->get()->count();

    // Page number and offset for infinite scroll
    $query->skip($startRow)->take(10);

    // Get our first set of tiles
    $tiles = $query->get();

    return Response::json(array(
        'count' => $count,
        'tiles' => $tiles->toArray()));
});

你看,我有一个充满“资源”的数据库,这些“资源”(通过数据透视表)与标签、cmets 和警报相关,我希望这些资源可以根据以下任何条件进行搜索: 资源模型本身包含的文本、与资源关联的标签以及关联警报的数量。

我遇到的一个问题是关键字搜索似乎不够“准确”。当我搜索“Venture Firm”时,在包含短语“Venture Firm”的结果之前会返回一些结果——用户肯定不会想到这一点。

我遇到的另一个问题与选择“显示”类型有关(即$query->has('alerts'),如果用户只想查看带有警报的资源)。如果我输入关键字搜索和显示类型(如上),结果仍将包含没有警报的资源(即使我指定我只想要有警报的资源)。

【问题讨论】:

    标签: laravel-4 eloquent query-builder


    【解决方案1】:

    相关性搜索取决于您的数据库引擎。

    但是对于关键字搜索,你就错了:

    foreach ($fields as $field)
    {
        $query->orWhere($field, 'LIKE', '%'. $term .'%');
    }
    

    这篇文章添加了WHERE ....long list of clauses here.... OR something LIKE %term% ...,这基本上破坏了整个事情。

    你需要这个:

    $fields = array(
     'resources.name',
     'resources.description',
     'resources.website',
     'resources.additional_info'
    );
    
    $query->where(function ($q) use ($searchTerms, $fields) {
      foreach ($searchTerms as $term)
      {
        foreach ($fields as $field)
        {
            $q->orWhere($field, 'LIKE', '%'. $term .'%');
        }
      }
    });
    

    这会将您的OR .. OR .. 子句包装在AND ( .. OR .. ) 中。

    【讨论】:

    • 谢谢 - 这有帮助!查看http://laravel.com/docs/queries#advanced-wheres 也很有用。您的代码中有一些语法错误,但我理解。
    • @cornflakes24 我认为那里没有任何语法错误,除了$q$query,这确实是错误的,并且也缺少括号。假设这就是你的意思
    • 是的,我就是这个意思!我猜$q$query 的混淆是语义错误,缺少括号是语法错误,不是吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 2013-08-29
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多