Skip to content

Commit

Permalink
add updated_since to v3 search api
Browse files Browse the repository at this point in the history
  • Loading branch information
laraferroni committed Jul 23, 2021
1 parent 4860c48 commit d4278a2
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 1 deletion.
1 change: 1 addition & 0 deletions app/controllers/api/v3/search.rb
Expand Up @@ -11,6 +11,7 @@ class Search < API::Base
optional :location, type: String, desc: "Location for proximity search", default: "IP"
optional :distance, type: String, desc: "Distance in miles from `location` for proximity search", default: 10
optional :stolenness, type: String, values: %w[non stolen proximity all] + [""], default: "stolen"
optional :updated_since, type: Integer, desc: "Date last updated in unicode date"
optional :query_items, type: Array, desc: "Our Fancy select query items, DO NOT USE, may change without notice", documentation: {hidden: true}
end
params :search do
Expand Down
15 changes: 14 additions & 1 deletion app/models/concerns/bike_searchable.rb
Expand Up @@ -10,6 +10,7 @@ module ClassMethods
# colors: array of colors, friendly found, faster if integers. Overrides query_items if passed explicitly
# manufacturer: friendly found, faster if integer. Overrides query_items if passed explicitly.
# stolenness: can be 'all', 'non', 'stolen', 'found', 'proximity'. Defaults to 'stolen'
# updated_since: can be a date
# location: location for proximity search. Only for stolenness == 'proximity'. 'ip'/'you' uses IP geocoding and returns location object
# distance: distance in miles for matches. Only for stolenness == 'proximity'
# bounding_box: bounding box generated by geocoder. Only for stolenness == 'proximity'
Expand All @@ -20,11 +21,11 @@ def searchable_interpreted_params(query_params, ip: nil)
params[:serial] = SerialNormalizer.new(serial: query_params[:serial]).normalized
params[:raw_serial] = query_params[:serial]
end

params
.merge(searchable_query_items_query(query_params)) # query if present
.merge(searchable_query_items_manufacturer(query_params)) # manufacturer if present
.merge(searchable_query_items_colors(query_params)) # color if present
.merge(searchable_query_items_updated_since(query_params)) # updated_since if present
.merge(searchable_query_stolenness(query_params, ip))
.to_h
end
Expand Down Expand Up @@ -77,6 +78,7 @@ def non_serial_matches(interpreted_params)
# For each of the of the colors, call searching_matching_color_ids with the color_id on the previous ;)
(interpreted_params[:colors] || [nil])
.reduce(self) { |matches, c_id| matches.search_matching_color_ids(c_id) }
.search_matching_update_since(interpreted_params[:updated_since])
.search_matching_stolenness(interpreted_params)
.search_matching_query(interpreted_params[:query])
.where(interpreted_params[:manufacturer] ? {manufacturer_id: interpreted_params[:manufacturer]} : {})
Expand Down Expand Up @@ -125,6 +127,12 @@ def searchable_query_stolenness(query_params, ip)
end
end

def searchable_query_items_updated_since(query_params)
#ignore anything that isn't a unicode integer date
return {updated_since: Time.at(query_params[:updated_since])} if query_params[:updated_since].is_a? Integer
return {}
end

def extracted_query_items_manufacturer_id(query_params)
return query_params[:manufacturer] if query_params[:manufacturer].present?
manufacturer_id = query_params[:query_items]&.select { |i| i.start_with?(/m_/) }
Expand Down Expand Up @@ -170,6 +178,11 @@ def search_matching_color_ids(color_id)
where("primary_frame_color_id=? OR secondary_frame_color_id=? OR tertiary_frame_color_id =?", color_id, color_id, color_id)
end

def search_matching_update_since(updated_since)
return all unless updated_since # So we can chain this if we don't have an updated since date
where("bikes.updated_at >= ?", updated_since)
end

def search_matching_query(query)
query.presence && pg_search(query) || all
end
Expand Down
7 changes: 7 additions & 0 deletions spec/factories/bikes.rb
Expand Up @@ -122,6 +122,13 @@
bike.reload
end
end
factory :older_stolen_bike, traits: [:stolen_trait] do
after(:create) do |bike|
create(:stolen_record, :in_chicago, bike: bike)
bike.update_attributes(:updated_at => Time.now - 1.year)
bike.reload
end
end

trait :organized_bikes do # don't use this trait, use the factories it's included with
transient do
Expand Down
8 changes: 8 additions & 0 deletions spec/models/stolen_bike_listing_spec.rb
Expand Up @@ -21,6 +21,7 @@
let(:stolen_bike_listing1) { FactoryBot.create(:stolen_bike_listing, primary_frame_color: color, listed_at: Time.current - 3.months) }
let(:stolen_bike_listing2) { FactoryBot.create(:stolen_bike_listing, secondary_frame_color: color, tertiary_frame_color: color2, listed_at: Time.current - 2.weeks) }
let(:stolen_bike_listing3) { FactoryBot.create(:stolen_bike_listing, tertiary_frame_color: color, manufacturer: manufacturer) }
let(:stolen_bike_listing4) { FactoryBot.create(:stolen_bike_listing, updated_at: Date.today - 1.year)}
let(:all_color_ids) do
[
stolen_bike_listing1.primary_frame_color_id,
Expand Down Expand Up @@ -57,6 +58,13 @@
expect(StolenBikeListing.search(interpreted_params).pluck(:id)).to eq([stolen_bike_listing3.id])
end
end
context "updated_since" do
let(:query_params) { {updated_since: Date.today - 1.week, stolenness: "all"} }
it "matches just the bikes updated in the past week" do
expect(StolenBikeListing.search(interpreted_params).count === 3)
expect(StolenBikeListing.search(interpreted_params).pluck(:id)).to eq([stolen_bike_listing3.id, stolen_bike_listing2.id, stolen_bike_listing1.id])
end
end
end
end

Expand Down
20 changes: 20 additions & 0 deletions spec/requests/api/v3/search_request_spec.rb
Expand Up @@ -20,6 +20,26 @@
end
end

describe "/" do
let!(:bike) { FactoryBot.create(:bike, updated_at: Time.now - 1.day) }
let!(:bike_old) { FactoryBot.create(:bike, updated_at: Time.now - 1.year) }
let!(:bike_stolen_old) { FactoryBot.create(:older_stolen_bike) }
let!(:bike_stolen) { FactoryBot.create(:stolen_bike, updated_at: Time.now - 1.day) }
let(:query_params) { {updated_since: (Time.now - 1.week).to_i, stolenness: "all"} }
context "with per_page" do
it "returns all matching bikes" do
expect(Bike.count).to eq 4
get "/api/v3/search", params: query_params.merge(format: :json)
expect(response.header["Total"]).to eq("2")
result = JSON.parse(response.body)
expect(result["bikes"][0]["id"]).to eq bike_stolen.id
expect(result["bikes"][1]["id"]).to eq bike.id
expect(response.headers["Access-Control-Allow-Origin"]).to eq("*")
expect(response.headers["Access-Control-Request-Method"]).to eq("*")
end
end
end

describe "/close_serials" do
let!(:bike) { FactoryBot.create(:bike, manufacturer: manufacturer, serial_number: "something") }
let(:query_params) { {serial: "somethind", stolenness: "non"} }
Expand Down

0 comments on commit d4278a2

Please sign in to comment.