如何加快WordPress中的GEO搜索速度

时间:2015-12-06 01:26:42

标签: mysql wordpress latitude-longitude wp-query

我正在使用“WP_GeoQuery”类,它扩展了WP_Query,因此我们可以通过英国邮政编码(lat和long坐标)搜索帖子。它工作得非常好,但速度非常慢。

使用以下代码扩展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)
    }

1 个答案:

答案 0 :(得分:0)

我无法找到扩展WP_Query类的解决方案,但我找到了另一种解决方案。

通过自定义页面调用搜索,调用新的WP_GeoQuery类而不是WP_Query。

只有2个帖子用于测试目的,但搜索过程非常缓慢。经过一些测试后,我设法确定了使进程变慢的代码。这是公众&#34; posts_fields&#34;从我的第一篇文章中的原始WP_GeoQuery类中的函数。这包含了Haversine公式。

我原本以为当我查询Google以获取邮政编码(英国)的纬度/长距离时,我可能会陷入困境。经过一些测试,甚至切换到postcodes.io API(顺便推荐),这肯定不是问题,它肯定在WordPress查询中并指向&#34; posts_fields&#34;功能

我继续研究一些插件,寻找进行纬度/经度搜索的替代方法。许多可用的Geo插件非常复杂,难以剖析和隔离代码,因此我可以在我的插件中使用它,并使用我自己的自定义主题和搜索结果页面。

我最终找到了一个插件来完成这项工作。它是一个小插件,允许我在我的主题中使用它的功能并解决问题,因为现在搜索速度很快。缺点是该插件似乎不再受支持,并且已经更新了2年。但它工作得很好。该插件是&#34; GEO数据存储&#34; - 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);

所以插件&#34; getPostIDsOfInRange&#34;函数获取属于给定纬度和经度半径的所有帖子ID。然后我们将这些ID放在&#34; post__in&#34;我们实现了WP_Query的参数。它很棒。

不是我想要的方式,因为扩展的WP_Query类似乎是最好的方法,但这是我发现的最快的解决方案。

我希望这有助于某人。