johnhamelink
3/14/2016 - 1:04 PM

events.rb

  def find_events_by_coordinate(event)
    return event if @coordinates.nil?
    return event if @coordinates['latitude'].nil?   || @coordinates['longitude'].nil?

    if @coordinates['latitude'].is_a? String
      @coordinates['latitude'] = @coordinates['latitude'].to_f
    end

    if @coordinates['longitude'].is_a? String
      @coordinates['longitude'] = @coordinates['longitude'].to_f
    end

    return event if @coordinates['latitude'].nan?   || @coordinates['longitude'].nan?
    return event if @coordinates['latitude'] == 0.0 || @coordinates['latitude'] == 0.0

    # Build query which defines a coordinate search based on search params
    venues_query = Venue.select('venues.id')
                   .distance_to_point(
                     srid: @coordinates['srid'],
                     lat:  @coordinates['latitude'],
                     lng:  @coordinates['longitude'],
                     distance: @coordinates['proximity_threshold']
                   )
                   .where_distance_within(
                     coordinate: {
                       srid: @coordinates['srid'],
                       lat:  @coordinates['latitude'],
                       lng:  @coordinates['longitude']
                     },
                     distance: @coordinates['proximity_threshold']
                   )
                   .order('distance ASC')

    # Build a CTE and use the coordinate search query's AST
    # as the logic within it.
    ordered_venues_cte = Arel::Table.new(:ordered_venues)
    ordered_venues = Arel::Nodes::As.new(
      ordered_venues_cte, venues_query.arel
    )

    # Build a CTE and use the event model joined with the coordinates search CTE
    # via the events_venues table.
    event_table = Event.arel_table
    events_venues_table = Arel::Table.new(:events_venues, ActiveRecord::Base)
    matching_events_cte = Arel::Table.new(:matching_events)
    matching_events = Arel::Nodes::As.new(
      matching_events_cte,
      event_table.project(
        'events.*',
        ordered_venues_cte[:id].as('venue_id'),
        ordered_venues_cte[:distance].as('distance')
      )
                 .join(events_venues_table)
                   .on(events_venues_table[:event_id].eq(event_table[:id]))
                 .join(ordered_venues_cte)
                   .on(events_venues_table[:venue_id].eq(ordered_venues_cte[:id]))
    )

    # Query the resultant CTE, ordering the results by distance
    events_query = Arel::Table.new('matching_events')
                   .project('matching_events.*')
                   .order('distance ASC')
                   .with(ordered_venues, matching_events)
    @queries += 1
    events_query
  end