module RailsConnector
# This class provides an implementation for accessing the Verity based search server.
# It should be customized by subclassing.
class VeritySearchRequest
attr_reader :query_string
# Sanitizes the given +query_string+ and takes +options+ for accessing the SES.
#
# +query_string+ is a VQL search query.
# +options+ is a hash and may be used to tweek the query.
#
# Options:
# :host:: The SES server host, default: 'localhost'
# :port:: The SES server port, default: 3011
# :offset:: The search offset, default: 0
# :limit:: The maximum number of hits in the SearchResult, default: 10
# :min_relevance:: The minimum relevance in percent, default: 50
# :max_docs:: The maximum number of documents to be searched, default: 'unlimited'
# :parser:: The VQL query parser to be used, default: 'simple'
# :sort_order:: The sort order of the hits, a list of key-value pairs, each consisting of the sortkey and the sort direction ("asc"=ascending, "desc"=descending), default: [["score", "desc"], ["name", "asc"]]
# :collections:: The collections to be searched, default: ['cm-contents']
# :base_query:: The VQL base query, default: nil
def initialize(query_string, options=nil)
@query_string = self.class.sanitize(query_string)
@options = Configuration.search_options.merge(options || {})
end
# Accesses the SES and fetches search hits.
#
# Uses the #base_query and +options+ given in #new.
def fetch_hits
SES::VerityAccessor.new(vql_query_for(@query_string), {:base_query => base_query}.merge(@options)).search
end
# Removes unwanted characters from +text+.
def self.sanitize(text) #:nodoc:
text.strip.gsub(/[<>"'ยด`,()\[\]{}=!@\\]/, '')
end
def vql_query_for(query_string = '')
words = query_string.split(/\s+/).map do |word|
word unless %w(and or not).include?(word.downcase)
end.compact.join(", ")
"<#AND> (#{words})"
end
# Combines base query conditions to a single base query (see #base_query_conditions).
#
# A base query is used to reduce the number of documents before executing the actual query.
# By default, all base query conditions must be met, they are combined using the +AND+ operator.
def base_query
"<#AND> (#{base_query_conditions.values.compact.join(', ')})"
end
# A hash of conditions, combined to a base query by #base_query.
# Note that all values of the hash must be valid VQL syntax.
# The keys have no meaning and exist only so single conditions can be replaced
# in a subclass:
#
# class SearchRequest < VeritySearchRequest
# def base_query_conditions
# super.merge(:content => '("edited" <#IN> state)'
# end
# end
def base_query_conditions
conditions = {}
conditions[:objTypes] = '<#ANY> (
("generic" <#IN> objType),
("document" <#IN> objType),
("publication" <#IN> objType)
)'
conditions[:content] = '("released" <#IN> state)'
conditions[:suppressExport] = '("0" <#IN> suppressExport)'
conditions[:validFrom] = "(validFrom < #{Time.now.to_iso})"
conditions[:validUntil] = %!<#OR> (
(validUntil = ""),
(validUntil > #{Time.now.to_iso})
)!
conditions
end
end
end