Class: Heatwave::WebSearch
- Inherits:
-
Object
- Object
- Heatwave::WebSearch
- Defined in:
- app/services/heatwave/web_search.rb
Overview
Provider-agnostic web (SERP) search.
Complement to Crawler: search finds URLs, the crawler reads
them. Backed by Oxylabs' parsed +google_search+ source through the shared
Retailer::OxylabsApi account client, so search spend shares the same
subscription and Retailer::DailyCostGuard daily ceiling as the retailer
probes.
This class is the abstraction seam callers (the Sunny +web_search+ tool,
SEO jobs, anything else) depend on — a second backend (Apify actor,
Perplexity, provider-native search) slots in behind #search without
touching callers.
Defined Under Namespace
Constant Summary collapse
- DEFAULT_LIMIT =
Organic results returned when the caller doesn't ask for a count.
8- MAX_LIMIT =
Hard ceiling on returned results (one SERP page carries ~10 organic).
20- SNIPPET_MAX_CHARS =
Snippets are for relevance triage, not reading — cap them.
320
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(client: Retailer::OxylabsApi.new) ⇒ WebSearch
constructor
A new instance of WebSearch.
- #search(query, geo_location: nil, limit: DEFAULT_LIMIT) ⇒ Object
Constructor Details
#initialize(client: Retailer::OxylabsApi.new) ⇒ WebSearch
Returns a new instance of WebSearch.
45 46 47 |
# File 'app/services/heatwave/web_search.rb', line 45 def initialize(client: Retailer::OxylabsApi.new) @client = client end |
Class Method Details
.search(query, geo_location: nil, limit: DEFAULT_LIMIT) ⇒ Response
40 41 42 |
# File 'app/services/heatwave/web_search.rb', line 40 def self.search(query, geo_location: nil, limit: DEFAULT_LIMIT) new.search(query, geo_location: geo_location, limit: limit) end |
Instance Method Details
#search(query, geo_location: nil, limit: DEFAULT_LIMIT) ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'app/services/heatwave/web_search.rb', line 50 def search(query, geo_location: nil, limit: DEFAULT_LIMIT) q = query.to_s.strip return Response.new(items: [], error: 'query is blank') if q.blank? payload = { source: 'google_search', query: q, parse: true } payload[:geo_location] = geo_location if geo_location.present? result = @client.request(payload) return Response.new(items: [], error: result.error.presence || 'search failed') unless result.success? Response.new(items: extract_items(result.data, limit: limit.to_i.clamp(1, MAX_LIMIT)), error: nil) end |