【问题标题】:Query N closest database entries using active record query interface使用活动记录查询接口查询 N 个最近的数据库条目
【发布时间】:2020-05-04 01:58:42
【问题描述】:

在我的 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 不是很熟悉,也不知道如何通过迁移来做到这一点。

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

谢谢。

【问题讨论】:

  • 这个来自 SO 的链接可能对你有帮助 stackoverflow.com/a/39338205/938947
  • 这行得通,但我想知道是否有一种不需要文字 SQL 的更简洁的方法,可以构建在活动记录查询接口之上。
  • 你用的是什么数据库?
  • 我正在使用 MySQL 特别是 InnoDB

标签: sql ruby-on-rails database activerecord geospatial


【解决方案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

但您可能想查看geocoder gem,它提供了此功能并使用了更好的公式。

【讨论】:

  • 谢谢,不敢相信如何通过迁移添加功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-25
相关资源
最近更新 更多