2014-08-29 56 views
0

我有一个有点令人费解的搜索查询,我想使效率更高(如果可能)。如何让此搜索查询更高效?

下面是该查询的全部代码:

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

你看,我都充满了“资源”,这(通过数据透视表)的数据库相关的标记,注释和提醒,我想这些资源可以通过以下任何标准进行搜索: 资源模型本身包含的文本,与资源关联的标记以及关联的警报数量。

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

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

回答

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 ..)

+0

谢谢 - 这有帮助!查看[http://laravel.com/docs/queries#advanced-wheres](http://laravel.com/docs/queries#advanced-wheres)也很有用。你的代码中有几个语法错误,但我明白。 – chipit24 2014-08-30 18:09:53

+0

@ cornflakes24我不认为这里有任何语法错误,除了'$ q'和'$ query',这确实是错误的,并且也缺少了parens。假设这就是你的意思 – 2014-08-30 19:22:51

+0

是的,这就是我的意思!我想''q'和'$ query'的混淆会是一个语义错误,缺少的括号会是一个语法错误,不是吗? – chipit24 2014-08-31 16:47:01