#ruby-on-rails #ruby #rest
Вопрос:
В настоящее время я выполняю задачу на своей работе, которую можно решить, только изменив старый код API, который у нас есть. Я никогда не использовал Ruby/Rails до вчерашнего дня, поэтому я сталкиваюсь с некоторыми трудностями, чтобы правильно понять, как это сделать, но я считаю, что это довольно просто.
В файле контроллера я смог создать новую конечную точку, которая возвращает всю информацию, доступную в нашей базе данных, через HTTP-запрос. Запрос, сделанный пользователем, отправляет два параметра: широту и долготу, и в результате будет получен список мест, отсортированных по расстоянию каждого места от этих координат. Код, который я использую для этого, выглядит следующим образом:
def myfunction
@host = "#{request.protocol}#{request.host_with_port}"
# Filters
@projects = Project.published
# Coordinates and distance
lat = geocode_params.fetch('latitude')
lon = geocode_params.fetch('longitude')
origin = Geokit::LatLng.new(lat, lon)
distance_sql = Project.distance_sql(origin)
# Getting closest places
if (params[:latitude] != nil)
ceil = params[:latitude].to_i 1
floor = params[:latitude].to_i - 1
@projects = @projects.where('official_lat > ? AND official_lat < ?',floor,ceil)
end
if (params[:longitude] != nil)
ceil = params[:longitude].to_i 1
floor = params[:longitude].to_i - 1
@projects = @projects.where('official_long > ? AND official_long < ?',floor,ceil)
end
@projects = @projects.page(params[:page]).per(@items_perpage).order("#{distance_sql} ASC").limit(15)
render json: @projects, meta: {count: @projects.total_count}, host: @host
end
Я скопировал этот «distance_sql» из другого фрагмента кода, но, насколько я понимаю, он генерирует новую переменную для каждого элемента в проекте с расстоянием в километрах между местом и координатами, переданными через HTTP-запрос.
Я просто хотел включить это расстояние в параметры, возвращаемые API. Поскольку он создается «на ходу», я не смог найти способ включить его в файл сериализатора.
Я также попытался добавить это в конец кода, но конечная точка перестала работать:
@projects = Project.includes(:distance_sql)
render :json => @projects.to_json(:include => :distance_sql), meta: {count: @projects.total_count}, host: @host
Возможно ли это сделать?
Заранее спасибо
РЕДАКТИРОВАТЬ: Я думал, что «sql_distance» вернул расстояние в км, потому что этот API используется для возврата всех мест в пределах 50 км, и в коде контроллера я нашел это:
@projects_latlong = Project.published.within(50, origin: [lat, lon]).order("#{distance_sql} ASC").limit(MAX_PROJECTS_TO_SHOW)
Я предположил, что это была команда для получения мест в радиусе 50 км, но я не уверен в этом на 100%. Я также попытался найти команду «внутри» в других файлах, но безуспешно.
Комментарии:
1. Данные, содержащиеся в этом
meta
ключе, включаются в ответ. Вы можете ознакомиться с документами здесь, они предоставленыactive_model_serializer
gem (см. rubydoc.info/gems/active_model_serializers/0.9.0 ). Однако я не уверен, как вы получаете «расстояние» в качестве возвращаемого числа … похоже, что это SQL-запрос здесь2. @max-pleaner Я думал включить в мета-ключ, но проблема в том, что объект «@проекты» представляет собой массив со всеми местами и их соответствующими параметрами, мета-ключ (я думаю) включает только информацию для «целого», например, общее количество элементов в «@проектах». Поскольку мне нужно было расстояние для каждого из них, я отказался от использования мета. distance_sql действительно является запросом, я думал, что он вернет номер, но после вашего комментария я заметил, что могу ошибаться. Я отредактирую вопрос, чтобы уточнить, что
3. Да, я думаю, вам нужно найти способ дистанцировать sql-запрос, чтобы возвращать расстояние для каждой записи, как только вы это сделаете, будет не так сложно вернуть его из ответа JSON. Возможно, вам потребуется сделать его виртуальным атрибутом модели или что-то в этом роде.
Ответ №1:
Если бы это было возможно, я бы использовал вместо этого драгоценный камень геокодера, который выполняет геопространственные запросы прямо из коробки:
class Project < ApplicationRecord
geocoded_by :address
end
Это позволяет вам сократить весь этот беспорядок до:
@projects = Project.published.then do |scope|
lat, lon = params[:latitude], params[:longitude]
if lat.present? amp;amp; lon.present?
scope.near(lat, lon).order(distance: :asc)
else
scope
end
end.page(params[:page])
.per(@items_perpage)
.limit(15)