lib/services/search.rb in Active-0.0.14 vs lib/services/search.rb in Active-0.0.17

- old
+ new

@@ -2,10 +2,11 @@ require 'json' require 'cgi' require 'rubygems' require 'mysql' require 'active_record' +require 'digest/md5' module Active module Services # we should remove this class and just replace with symbols @@ -28,24 +29,24 @@ end end class Search attr_accessor :api_key, :start_date, :end_date, :location, :channels, :keywords, :search, :radius, :limit, :sort, :page, :offset, :latitude, :longitude, - :view, :facet, :sort, :num_results, :asset_ids, :dma + :view, :facet, :sort, :num_results, :asset_ids, :dma, :city, :state, :country, :bounding_box attr_reader :results, :endIndex, :pageSize, :searchTime, :numberOfResults, :end_point, :meta SEARCH_URL = "http://search.active.com" DEFAULT_TIMEOUT = 60 def initialize(data={}) self.api_key = data[:api_key] || "" - self.location = data[:location] || "" + self.location = data[:location] self.zips = data[:zips] || [] self.channels = data[:channels] || [] self.keywords = data[:keywords] || [] - self.radius = data[:radius] || "50" + self.radius = data[:radius] || nil self.limit = data[:limit] || "10" self.sort = data[:sort] || Sort.DATE_ASC self.page = data[:page] || "1" self.offset = data[:offset] || "0" self.view = data[:view] || "json" @@ -57,10 +58,14 @@ self.asset_ids = data[:asset_ids] || [] self.asset_id = data[:asset_id] || "" self.latitude = data[:latitude] self.longitude = data[:longitude] self.dma = data[:dma] + self.city = data[:city] + self.state = data[:state] + self.country = data[:country] + self.bounding_box = data[:bounding_box] end # Example # Search.search( {:zips => "92121, 92078, 92114"} ) # or @@ -76,10 +81,11 @@ def zips @zips end def location=(value) + return if value.nil? @location = CGI.escape(value) end def keywords=(value) if value.class == String @@ -98,18 +104,29 @@ end def asset_ids=(value) @asset_ids = value end + + def bounding_box=(value) + return if value==nil + value = HashWithIndifferentAccess.new(value) + if value.has_key?("sw") && value.has_key?("ne") + @bounding_box=value + else + raise "bounding_box must be hash with keys sw and ne" + end + end def asset_id=(value) return if value.empty? @asset_ids<<value end def end_point - meta_data = "" + meta_data = "" + loc_str = "" # CHANNELS channel_keys = [] @channels.each do |c| c.to_sym if Categories.CHANNELS.include?(c) @@ -125,20 +142,31 @@ temp_ids << "meta:" + CGI.escape("assetId=#{id.gsub("-","%2d")}") end meta_data += temp_ids.join("+OR+") end # LOCATION - # 1 Look for zip codes - # 2 Look for lat lng - # 3 Look for a formatted string "San Diego, CA, US" - # 4 Look for a dma - if not @zips.empty? + # 1 Look for :city, :state, :country + # 2 Look for zip codes + # 3 Look for lat lng + # 4 Look for a formatted string "San Diego, CA, US" + # 5 Look for a dma + if @city or @state or @country + if @city + meta_data += "+AND+" unless meta_data == "" + meta_data += "meta:city=#{Search.double_encode_channel(@city)}" + end + if @state + meta_data += "+AND+" unless meta_data == "" + meta_data += "meta:state=#{Search.double_encode_channel(@state)}" + end + elsif !@zips.empty? + # if not @zips.empty? loc_str = @zips.join(",") elsif @latitude and @longitude loc_str = "#{@latitude};#{@longitude}" elsif @dma - loc_str = "" + meta_data += "+AND+" unless meta_data == "" meta_data += "meta:dma=#{Search.double_encode_channel(@dma)}" else loc_str = @location end @@ -152,21 +180,55 @@ if @end_date.class == Date @end_date = URI.escape(@end_date.strftime("%m/%d/%Y")).gsub(/\//,"%2F") end meta_data += "meta:startDate:daterange:#{@start_date}..#{@end_date}" - url = "#{SEARCH_URL}/search?api_key=#{@api_key}&num=#{@num_results}&page=#{@page}&l=#{loc_str}&f=#{@facet}&v=#{@view}&r=#{@radius}&s=#{@sort}&k=#{@keywords.join("+")}&m=#{meta_data}" + # BOUNDING BOX + if @bounding_box!=nil + #The values in the GSA metadata are shifted to prevent negative values. This was done b/c lat/long + # are searched as a number range and the GSA doesn't allow negative values in number ranges. + # We shift latitude values by 90 and longitude values by 180. + + if @bounding_box[:sw].class==String + #String :bounding_box => { :sw => "37.695141,-123.013657", :ne => "37.832371,-122.356979"} + latitude1 = @bounding_box[:sw].split(",").first.to_f+90 + latitude2 = @bounding_box[:ne].split(",").first.to_f+90 + longitude1 = @bounding_box[:sw].split(",").last.to_f+180 + longitude2 = @bounding_box[:ne].split(",").last.to_f+180 + else + #hash query[:bounding_box] = { :sw => [123, 10], :ne => [222,10] } + latitude1 = @bounding_box[:sw].first.to_f+90 + latitude2 = @bounding_box[:ne].first.to_f+90 + longitude1 = @bounding_box[:sw].last.to_f+180 + longitude2 = @bounding_box[:ne].last.to_f+180 + end + meta_data += "+AND+" unless meta_data == "" + meta_data += "meta:latitudeShifted:#{latitude1}..#{latitude2}+AND+meta:longitudeShifted:#{longitude1}..#{longitude2}" + end + + # url = "#{SEARCH_URL}/search?api_key=#{@api_key}&num=#{@num_results}&page=#{@page}&l=#{loc_str}&f=#{@facet}&v=#{@view}&r=#{@radius}&s=#{@sort}&k=#{@keywords.join("+")}&m=#{meta_data}" + urla = ["#{SEARCH_URL}/search?api_key=#{@api_key}"] + urla << "num=#{@num_results}" if @num_results + urla << "page=#{@page}" if @page + urla << "l=#{loc_str}" unless loc_str.nil? or loc_str.empty? + urla << "f=#{@facet}" if @facet + urla << "v=#{@view}" if @view + urla << "r=#{@radius}" if @radius + urla << "s=#{@sort}" if @sort + urla << "k=#{@keywords.join("+")}" if @keywords and !@keywords.empty? + urla << "m=#{meta_data}" if meta_data + + return urla.join("&") end def search searchurl = URI.parse(end_point) req = Net::HTTP::Get.new(searchurl.path) http = Net::HTTP.new(searchurl.host, searchurl.port) http.read_timeout = DEFAULT_TIMEOUT - puts "#{searchurl.path}?#{searchurl.query}" - + puts "Active Search [GET] #{"#{searchurl.path}?#{searchurl.query}"}" res = http.start { |http| http.get("#{searchurl.path}?#{searchurl.query}") } if (200..307).include?(res.code.to_i) @@ -174,11 +236,14 @@ parsed_json = JSON.parse(res.body) @endIndex = parsed_json["endIndex"] @pageSize = parsed_json["pageSize"] @searchTime = parsed_json["searchTime"] @numberOfResults = parsed_json["numberOfResults"] - @results = parsed_json['_results'].collect { |a| Activity.new(a) } + @results = parsed_json['_results'].collect { |a| Activity.new(GSA.new(a)) } + + Active.CACHE.set( Digest::MD5.hexdigest(end_point), self) if Active.CACHE + rescue JSON::ParserError => e raise RuntimeError, "JSON::ParserError json=#{res.body}" @endIndex = 0 @pageSize = 0 @searchTime = 0 @@ -221,14 +286,33 @@ # # # http://developer.active.com/docs/Activecom_Search_API_Reference # returns an array of results and query info def self.search(data=nil) - search = Search.new(data) - search.search - return search + search = Search.new(data) + search_hash = Digest::MD5.hexdigest(search.end_point) + puts "search_hash #{search_hash}" + cache = Search.return_cached(search_hash) + if cache != nil + return cache + else + search.search + return search + end end + + def self.return_cached key + if Active.CACHE + cached_version = Active.CACHE.get(key) + if cached_version + puts "Active Search [CACHE] #{key}" + return cached_version + end + end + nil + end + def [] @results end @@ -237,10 +321,9 @@ str = URI.escape(str, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")) str = URI.escape(str, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")) str.gsub!(/\-/,"%252D") str end - end # TODO move to a reflection service class Categories def self.CHANNELS \ No newline at end of file