2015-12-06 6 views
2

Я использую класс WP_GeoQuery, который расширяет WP_Query, чтобы мы могли искать сообщения по британскому почтовому индексу (лат и длинные координаты). Он работает очень хорошо, но он невероятно медленный.Как ускорить поиск GEO в WordPress

Есть ли какие-либо шаги, которые я могу предпринять при расширении 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

Сколько сообщений имеет значения lat/long в post_meta в вашей WP-установке? Десятки? Сотни? Десятки тысяч? –

+0

Только две должности в данный момент. Это в настоящее время находится на этапах тестирования. Несмотря на то, что я хорошо разбираюсь в WordPress, мне не хватает знаний в оптимизации MySQl, и я предполагаю, что скорость этого запроса имеет проблемы. По умолчанию поиск по WordPress происходит очень быстро, но как только я использую этот класс, который изменяет WP_Query, загрузка занимает 1 минуту, хотя есть только две записи для запроса. – Steven

+0

Я думал о вашей проблеме. Очень странно, что запрос только для двух лат/длинных точек занимает так много времени. Интересно, можете ли вы получить доступ к экземпляру MySQL с помощью phpmyadmin или какого-либо другого клиента MySQL * во время выполнения запроса, * получить текст запроса с помощью 'SHOW FULL PROCESSLIST' и опубликовать текст запроса здесь. –

ответ

0

Мне не удалось найти решение для расширенного класса WP_Query, но я нашел альтернативное решение.

Поиск вызывался через пользовательскую страницу с вызовом нового класса WP_GeoQuery вместо WP_Query.

Для тестирования было всего 2 сообщения, но процесс поиска был очень медленным. После некоторого тестирования мне удалось указать код, который замедлял процесс. Это публичная функция posts_fields в исходном классе WP_GeoQuery из моего первого сообщения. Это содержало формулу Хаверсина.

Первоначально я думал, что это может застрять, когда я запросил Google, чтобы получить лат/длинные расстояния в почтовом индексе (Великобритании). После некоторого тестирования и даже перехода на API-интерфейс postcodes.io (рекомендуется кстати) это определенно не показалось проблемой, и это определенно было в рамках запроса WordPress и указало на функцию posts_fields.

Я продолжал изучать некоторые плагины, ища альтернативные способы выполнения поиска по длине/длине. Многие доступные плагины Geo очень сложны и сложны для анализа и изоляции кода, поэтому я могу использовать его в своем плагине и с помощью собственной темы и результатов поиска.

В конце концов я нашел плагин, чтобы сделать эту работу. Это небольшой плагин и позволяет мне использовать его функции в моей теме и решает проблему, так как поиск выполняется быстро. Нижняя сторона заключается в том, что плагин, похоже, больше не поддерживается, и он не обновляется в течение 2 лет. Но он работает красиво. Плагин является «GEO Data Store» - 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» функция получает все идентификаторы сообщений, которые попадают в радиус заданной широты и долготы. Затем мы помещаем эти идентификаторы в параметр «post__in» WP_Query, и мы достигаем цели. Он отлично работает.

Не так, как я хотел это сделать, поскольку расширенный класс WP_Query кажется лучшим, но это самое быстрое решение, которое я нашел.

Надеюсь, это поможет кому-то.