2014-08-29 55 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