0

在我的 Rails 项目中,我有一个名为“items”的表,其中每个项目都有一个纬度和经度属性。当我为我的客户检索项目时,我会获取当前用户的经度和纬度。使用这两组坐标,我想使用一个半正弦函数并以最小的半正弦距离获取 100 个结果。

SQL 应如下所示:

SELECT
*,
haversine_distance(user_lat, user_long, item_lat, item_long) as distance
FROM
db.items
WHERE
item_lat < {user_lat} +1
AND item_lat > {user_lat}+1
AND ..
AND ..
ORDER BY distance DESC
LIMIT 100

但这需要在查询中使用自定义函数,我不知道该怎么做。此外,关于查询的第 7 行和第 8 行,我正在尝试使用 where 子句来快速过滤某个合理距离内的结果集,这样我就不必将 hasrsine 应用于整个表。现在,我在一个名为 DistanceService 的单例中定义了函数,但我假设我不能在查询中使用 Ruby 函数。

当我尝试将函数添加haversine_distance到控制器并调用它时:

      @items = Item.select("items.*, haversine_distance(geo_long, geo_lat,
                                  #{@current_user.geo_long}, #{@current_user.geo_lat}) AS distance")
                   .where(status: 'available', ....)

我遇到了 FUNCTION ****_api_development.haversine_distance does not exist。这使我认为该函数应该首先通过一些迁移在 SQL 中以某种方式定义,但我对 SQL 不是很熟悉,也不知道如何通过迁移来做到这一点。

我还想知道是否有支持分页的解决方案,因为它有望最终成为我项目的要求。

谢谢。

4

1 回答 1

1

在 Rails 中,您通过迁移定义数据库函数:

class AddHaversineDistanceFunction < ActiveRecord::Migration
  def up
    execute <<~SQL
      DELIMITER $$
      DROP FUNCTION IF EXISTS haversine_distance$$

      CREATE FUNCTION haversine_distance(
        lat1 FLOAT, lon1 FLOAT,
        lat2 FLOAT, lon2 FLOAT
      ) RETURNS FLOAT
      NO SQL DETERMINISTIC
      COMMENT 'Returns the distance in km
             between two known points of latitude and longitude'
      BEGIN
      RETURN DEGREES(ACOS(
        COS(RADIANS(lat1)) *
        COS(RADIANS(lat2)) *
        COS(RADIANS(lon2) - RADIANS(lon1)) +
        SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))
      )) * 111.045;
      END$$

      DELIMITER;
    SQL
  end

  def down
    execute "DROP FUNCTION IF EXISTS haversine_distance"
  end
end

这里的实际功能改编自Plum Island Media

但是您可能想查看提供此功能并使用更好公式的地理编码器 gem 。

于 2020-05-04T16:32:59.250 回答