Ruby on Rails: включить параметр в экземпляр на контроллере

#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)