2015-12-06 46 views
2

我使用的是WP_GeoQuery類,它擴展了WP_Query,因此我們可以通過英國郵政編碼(經緯度和長度座標)搜索帖子。它工作得很好,但速度非常慢。如何在WordPress中加速GEO搜索

當使用下面的代碼擴展WP_Query以加速MySQL請求時,我可以採取哪些步驟?

class WP_GeoQuery extends WP_Query 
    { 
     private $_search_latitude = NULL; 
     private $_search_longitude = NULL; 
     private $_radius = NULL; 

     /** 
     * Constructor - adds necessary filters to extend Query hooks 
     */ 
     public function __construct($args = array()) 
     { 
      // Extract Latitude 
      if(!empty($args['latitude'])) { $this->_search_latitude = $args['latitude']; } 
      // Extract Longitude 
      if(!empty($args['longitude'])) { $this->_search_longitude = $args['longitude']; } 
      // Extract Longitude 
      if(!empty($args['radius'])) { $this->_radius = $args['radius']; } 
      // unset lat/long 
      unset($args['latitude'], $args['longitude'], $args['radius']); 

      // Extract Post type 
      if(!empty($args['post_type'])) { $this->_post_type = $args['post_type']; } 

      add_filter('posts_fields', array($this, 'posts_fields'), 10, 2); 
      add_filter('posts_join', array($this, 'posts_join'), 10, 2); 
      add_filter('posts_where', array($this, 'posts_where'), 10, 2); 
      add_filter('posts_orderby', array($this, 'posts_orderby'), 10, 2); 
      add_filter('posts_distinct', array($this, 'posts_distinct'), 10, 2); 

      // Run query 
      parent::query($args); 

      // Remove filters so only WP_GeoQuery queries this way 
      remove_filter('posts_fields', array($this, 'posts_fields')); 
      remove_filter('posts_join', array($this, 'posts_join')); 
      remove_filter('posts_where', array($this, 'posts_where')); 
      remove_filter('posts_orderby', array($this, 'posts_orderby')); 
      remove_filter('posts_distinct', array($this, 'posts_distinct')); 

     } // END public function __construct($args = array()) 

     /** 
     * Return only distinct results 
     */ 
     function posts_distinct() 
     { 
      return "DISTINCT"; 
     } // END public function posts_distinct() 

     /** 
     * Selects the distance from a haversine formula 
     */ 
     public function posts_fields($fields) 
     { 
      global $wpdb; 

      if(!empty($this->_search_latitude) && !empty($this->_search_longitude)) 
      { 
       $fields .= sprintf(", (3959 * acos(
         cos(radians(%s)) * 
         cos(radians(latitude.meta_value)) * 
         cos(radians(longitude.meta_value) - radians(%s)) + 
         sin(radians(%s)) * 
         sin(radians(latitude.meta_value)) 
        )) AS distance ", $this->_search_latitude, $this->_search_longitude, $this->_search_latitude); 
      } 

      $fields .= ", latitude.meta_value AS latitude "; 
      $fields .= ", longitude.meta_value AS longitude "; 
      $fields .= ", location.meta_value AS location "; 

      return $fields; 
     } // END public function posts_join($join, $query) 

     /** 
     * Makes joins as necessary in order to select lat/long metadata 
     */ 
     public function posts_join($join, $query) 
     { 
      global $wpdb; 

      $join .= " INNER JOIN {$wpdb->postmeta} AS latitude ON {$wpdb->posts}.ID = latitude.post_id "; 
      $join .= " INNER JOIN {$wpdb->postmeta} AS longitude ON {$wpdb->posts}.ID = longitude.post_id "; 
      $join .= " INNER JOIN {$wpdb->postmeta} AS location ON {$wpdb->posts}.ID = location.post_id "; 

      return $join; 
     } // END public function posts_join($join, $query) 

     /** 
     * Adds where clauses to compliment joins 
     */ 
     public function posts_where($where) 
     { 

      $where .= ' AND latitude.meta_key="postcode_lat" '; 
      $where .= ' AND longitude.meta_key="postcode_lng" '; 
      $where .= ' AND location.meta_key="individual_details_postcode" '; 

      if(!empty($this->_search_latitude) && !empty($this->_search_longitude) && !empty($this->_radius)) 
      { 
       if(is_numeric($this->_radius)) 
       { 
        $where .= sprintf(' HAVING distance <= %s ', $this->_radius); 
       } 
      } 

      return $where; 
     } // END public function posts_where($where) 

     /** 
     * Adds where clauses to compliment joins 
     */ 
     public function posts_orderby($orderby) 
     { 
      if(!empty($this->_search_latitude) && !empty($this->_search_longitude)) 
      { 
       $orderby = " distance ASC, " . $orderby; 
      } 

      return $orderby; 
     } // END public function posts_orderby($orderby) 
    } 
+0

在WP安裝中post_meta中有多少帖子具有lat/long值?許多?數百?成千上萬? –

+0

此刻只有兩篇文章。這目前正處於測試階段。雖然我有經驗的開發與WordPress的我的知識缺乏MySQl優化,我假設這是查詢的速度有問題。默認的WordPress搜索速度非常快,但只要我使用修改WP_Query的這個類,它將花費1分鐘來加載,即使只有兩個帖子需要查詢。 – Steven

+0

我一直在想你的問題。奇怪的是,只有兩個經緯度的查詢需要這麼長時間。我不知道你是否可以在查詢運行時用phpmyadmin或其他MySQL客戶端*訪問你的MySQL實例,*用'SHOW FULL PROCESSLIST'檢索查詢文本,並在這裏發佈查詢文本。 –

回答

0

我無法找到擴展WP_Query類的解決方案,但我找到了替代解決方案。

正在通過調用新WP_GeoQuery類而不是WP_Query的調用通過自定義頁面調用搜索。

只有2個帖子用於測試目的,但搜索過程非常緩慢。經過一些測試後,我成功地將代碼放在了緩慢的過程中。這是我第一篇文章中原始WP_GeoQuery類中的公共「posts_fields」函數。這包含Haversine公式。

我原本以爲當我查詢Google獲取正在搜索的郵政編碼(英國)的經緯度時,它可能會卡住。經過一些測試甚至切換到postcodes.io API(推薦的方式),這絕對不是問題,它絕對在WordPress查詢內,並指向「posts_fields」函數。

我繼續研究一些插件,尋找替代方法來進行經緯度搜索。很多可用的Geo插件都非常複雜,難以剖析和隔離代碼,所以我可以在我的插件中使用它,並使用我自己的自定義主題和搜索結果頁。

我終於找到了一個插件,使這項工作,雖然。這是一個小插件,允許我在主題中使用它的功能並解決問題,因爲搜索速度很快。不利的一面是該插件似乎不再受到支持,並且它已經2年未更新。但它工作得很好。該插件是「全球環境展望數據商店」 - https://wordpress.org/plugins/geo-data-store/

所以我不再使用自定義的查詢類,而是以下面的方式與這個插件激活使用WP_Query:

$sc_gds = new sc_GeoDataStore(); 
$ids = (array) $sc_gds->getPostIDsOfInRange($post_type, $radius, $latitude, $longitude); 

$args= array(
    'post__in'   => $ids, 
    'posts_per_page' => 10, 
    'post_status'  => 'any', 
    'post_type'   => $post_type 
); 

$new_query = new WP_Query($args); 

所以該插件的「getPostIDsOfInRange」函數獲取落在給定經度和緯度範圍內的所有帖子ID。然後,我們將這些ID放在WP_Query的「post_in」參數中,我們實現了目標。它效果很好。

不是我想要做到的,因爲擴展的WP_Query類似乎是最好的方法,但這是我找到的最快的解決方案。

我希望這可以幫助別人。