如何加快 WordPress 中的 GEO 搜索

Posted

技术标签:

【中文标题】如何加快 WordPress 中的 GEO 搜索【英文标题】:How to speed up GEO search in WordPress 【发布时间】:2016-03-10 20:07:12 【问题描述】:

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

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

【问题讨论】:

在您的 WP 安装中,post_meta 中有多少帖子具有 lat/long 值?许多?数百?几万? 目前只有两个帖子。这目前处于测试阶段。尽管我有使用 WordPress 进行开发的经验,但我对 MySQl 优化的知识缺乏,我假设这是该查询的速度存在问题的地方。默认的 WordPress 搜索非常快,但是一旦我使用这个修改 WP_Query 的类,即使只有两个要查询的帖子,加载也需要 1 分钟以上。 我一直在考虑你的问题。很奇怪,只查询两个纬度/经度点需要这么长时间。我想知道您是否可以在查询运行时使用 phpmyadmin 或其他 MySQL 客户端访问您的 MySQL 实例, 使用 SHOW FULL PROCESSLIST 检索查询文本,然后在此处发布查询文本。 你怎么称呼搜索? 【参考方案1】:

我无法找到扩展 WP_Query 类的解决方案,但我找到了替代解决方案。

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

只有 2 个帖子用于测试目的,但搜索过程非常缓慢。经过一些测试后,我设法找出了减慢进程的代码。这是我第一篇文章中原始 WP_GeoQuery 类中的公共“posts_fields”函数。这包含Haversine 公式。

当我查询 Google 以获取正在搜索的邮政编码(英国)的经纬度时,我最初认为它可能会卡住。经过一些测试,甚至切换到 postcodes.io API(顺便推荐)后,这绝对不是问题,它肯定在 WordPress 查询中并指向“posts_fields”函数。

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

我最终还是找到了一个插件来完成这项工作。这是一个小插件,允许我在我的主题中使用它的功能并解决问题,因为现在搜索速度很快。不利的一面是该插件似乎不再受支持,并且已经有 2 年没有更新了。但它运作良好。该插件是“GEO 数据存储” - 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 类似乎是最好的方法,但这是我找到的最快的解决方案。

我希望这对某人有所帮助。

【讨论】:

以上是关于如何加快 WordPress 中的 GEO 搜索的主要内容,如果未能解决你的问题,请参考以下文章

弹性搜索 GeoIp 位置不是 geo_point 类型

Wordpress主题中的相对图像链接

JavaScript WordPress Geo Mashup Mapstraction自定义标记

PHP WordPress Geo Mashup - 自动添加地图到帖子的插件

WordPress Geo Mashup-自动向帖子添加地图的插件

php facetwp index geo my wordpress latitude / longitute