Class: FetchUtil::SearchTransport
- Inherits:
-
Object
- Object
- FetchUtil::SearchTransport
- Defined in:
- lib/fetch_util/search_transport.rb
Overview
Fetches and parses supported search result pages without involving the browser fetcher.
Defined Under Namespace
Classes: Candidate, HttpClient, HttpFailure, HttpResponse, SourceResponse
Constant Summary collapse
- SOURCES =
{ "brave" => { url: "https://search.brave.com/search?q=%{query}", hosts: %w[search.brave.com] }, "bing" => { url: "https://www.bing.com/search?q=%{query}&setlang=en-US&cc=US", hosts: %w[www.bing.com cn.bing.com] }, "duckduckgo" => { url: "https://html.duckduckgo.com/html/?q=%{query}", hosts: %w[html.duckduckgo.com] }, "google" => { url: "https://www.google.com/search?q=%{query}", hosts: %w[www.google.com] }, "ecosia" => { url: "https://www.ecosia.org/search?q=%{query}", hosts: %w[www.ecosia.org] }, "yahoo" => { url: "https://search.yahoo.com/search?p=%{query}", hosts: %w[search.yahoo.com] } }.freeze
- DEFAULT_TIMEOUT =
10.0- YAHOO_RECOVERY_ATTEMPTS =
2- FAILURE_REASONS =
%w[challenge failed host http_status parse query_mismatch redirect size timeout].freeze
- RELEVANCE_HEALTH_CANDIDATE_COUNT =
3- QUERY_FUNCTION_WORDS =
%w[ a an and are at be been being but by can could did do does for from had has have how i if in is it me my no not of on or our shall should that the their them then there these they this to was we were what when where which who why will with would you your ].freeze
- SCOPED_QUERY_TERM =
/(?:\A|\s)[+-]?[[:alpha:]][[:alnum:]_-]*:(?:"[^"]*"|'[^']*'|\S+)/- NEGATED_QUERY_TERM =
/(?:\A|\s)-\S+/- WRAPPER_HOSTS =
{ "bing" => %w[bing.com www.bing.com cn.bing.com], "duckduckgo" => %w[duckduckgo.com www.duckduckgo.com html.duckduckgo.com], "google" => %w[google.com www.google.com], "yahoo" => %w[r.search.yahoo.com] }.freeze
Class Method Summary collapse
- .candidate_matches_query?(query, candidate) ⇒ Boolean
- .candidates_match_query?(query, candidates) ⇒ Boolean
Instance Method Summary collapse
-
#initialize(sources: SOURCES.keys, timeout: DEFAULT_TIMEOUT, clock: nil, http_client: nil, html_parser: nil) ⇒ SearchTransport
constructor
A new instance of SearchTransport.
- #search(query, timeout: @timeout) ⇒ Object
Constructor Details
#initialize(sources: SOURCES.keys, timeout: DEFAULT_TIMEOUT, clock: nil, http_client: nil, html_parser: nil) ⇒ SearchTransport
Returns a new instance of SearchTransport.
57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/fetch_util/search_transport.rb', line 57 def initialize(sources: SOURCES.keys, timeout: DEFAULT_TIMEOUT, clock: nil, http_client: nil, html_parser: nil) @sources = sources.map(&:to_s).freeze unknown = @sources - SOURCES.keys raise ArgumentError, "unknown search sources: #{unknown.join(", ")}" if unknown.any? @timeout = Float(timeout) raise ArgumentError, "timeout must be positive" unless @timeout.positive? @clock = clock || -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) } @http_client = http_client || HttpClient.new(clock: @clock) @html_parser = html_parser || ->(body) { Nokogiri::HTML(body) } end |
Class Method Details
.candidate_matches_query?(query, candidate) ⇒ Boolean
76 77 78 79 80 81 82 |
# File 'lib/fetch_util/search_transport.rb', line 76 def self.candidate_matches_query?(query, candidate) query_terms = meaningful_query_terms(normalized_query_text(query)) return true if query_terms.length < 3 candidate_terms = normalized_terms("#{candidate.title} #{candidate.snippet}") (query_terms & candidate_terms).length >= 2 end |
.candidates_match_query?(query, candidates) ⇒ Boolean
70 71 72 73 74 |
# File 'lib/fetch_util/search_transport.rb', line 70 def self.candidates_match_query?(query, candidates) candidates.first(RELEVANCE_HEALTH_CANDIDATE_COUNT).any? do |candidate| candidate_matches_query?(query, candidate) end end |
Instance Method Details
#search(query, timeout: @timeout) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/fetch_util/search_transport.rb', line 84 def search(query, timeout: @timeout) query = query.to_s.strip raise ArgumentError, "query must not be empty" if query.empty? request_timeout = Float(timeout) raise ArgumentError, "timeout must be positive" unless request_timeout.positive? deadline = clock.call + request_timeout responses = Array.new(sources.length) threads = sources.each_with_index.map do |source, index| Thread.new { responses[index] = search_source(source, query, deadline) } end threads.each(&:join) responses end |