如何使此搜索查询更有效?

时间:2014-08-29 18:48:38

标签: laravel-4 eloquent query-builder

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

以下是此查询的完整代码:

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()));
});

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

我遇到的一个问题是关键字搜索似乎不够“准确”。当我搜索“Venture Firm”时,在包含“Venture Firm”这一短语之前返回了一些结果 - 用户绝对不会期望这样。

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

1 个答案:

答案 0 :(得分: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 .. )

相关问题