Skip to content

Instantly share code, notes, and snippets.

@robertknight
Forked from robhurring/search_terms.rb
Created March 16, 2012 11:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robertknight/2049623 to your computer and use it in GitHub Desktop.
Save robertknight/2049623 to your computer and use it in GitHub Desktop.
# Search term parser from https://gist.github.com/1477730
# Modified to allow periods (and other non-letter chars) in unquoted field values
# and field names.
#
# Helper class to help parse out more advanced saerch terms
# from a form query
#
# Note: all hash keys are downcased, so ID:10 == {'id' => 10}
# you can also access all keys with methods e.g.: terms.id = terms['id'] = 10
# this doesn't work with query as thats reserved for the left-over pieces
#
# Usage:
# terms = SearchTerms.new('id:10 search terms here')
# => @query="search terms here", @parts={"id"=>"10"}
# => terms.query = 'search terms here'
# => terms['id'] = 10
#
# terms = SearchTerms.new('name:"support for spaces" state:pa')
# => @query="", @parts={"name"=>"support for spaces", "state"=>"pa"}
# => terms.query = ''
# => terms['name'] = 'support for spaces'
# => terms.name = 'support for spaces'
#
# terms = SearchTerms.new('state:pa,nj,ca')
# => @query="", @parts={"state"=>["pa","nj","ca"]}
#
# terms = SearchTerms.new('state:pa,nj,ca', false)
# => @query="", @parts={"state"=>"pa,nj,c"}
#
# Useful to drive custom logic in controllers
class SearchTerms
attr_reader :query, :parts
# query:: this is what you want tokenized
# split:: if you'd like to split values on "," then pass true
def initialize(query, split = true)
@query = query
@parts = {}
@split = split
parse_query!
end
def [](key)
@parts[key]
end
private
def parse_query!
tmp = []
# pattern for field names and values which are not part of a field
fieldname_regex = '(?:([^\s:]+))'
# pattern for field values which are not enclosed in quotes
unquoted_value_regex = '[^\s]+'
# pattern for field values which are enclosed in quotes
quoted_value_regex = '"(?:.+|[^\"])*"'
@query.scan(/#{fieldname_regex}(?::(#{unquoted_value_regex}|(?:#{quoted_value_regex})))?/).map do |key,value|
if value.nil?
tmp << key
else
key.downcase!
@parts[key] = clean_value(value)
define_metaclass_method(key){ @parts[key] } unless key == 'query'
end
end
@query = tmp.join(' ')
end
def clean_value(value)
return value.tr('"', '') if value.include?('"')
return value.split(',') if @split && value.include?(',')
return true if value == 'true'
return false if value == 'false'
return value.to_i if value =~ /^[1-9][0-9]*$/
value
end
def define_metaclass_method(method, &block)
(class << self; self; end).send :define_method, method, &block
end
end
require './search_terms.rb'
require 'test/unit'
class TestStacktrace < Test::Unit::TestCase
def test_parse
# map of test_case_name => [input, expected_query, expected_fields]
cases = {
"simple" => ["foo","foo",{}],
"simple-field" => ["one:two","",{"one" => "two"}],
"term-with-period" => ["1.5","1.5",{}],
"multiple-fields" => ["one:two three:four","",{"one" => "two", "three" => "four"}],
"int-parse" => ["id:123","",{"id" => 123}],
"int-parse-leading-letter" => ["id:a01","","id" => "a01"],
"int-parse-leading-zero" => ["id:001","","id" => "001"],
"mixed-fields-terms" => ["one two:three four five:six","one four",{"two" => "three", "five" => "six"}]
}
cases.each do |name, args|
input, query, parts = args
terms = SearchTerms.new(input)
assert_equal(query, terms.query)
assert_equal(parts, terms.parts)
end
end
end
# basic usage to search users from your #index action
class UsersController < ApplicationController
def index
if params[:q]
terms = SearchTerms.new(params[:q])
if terms['id']
return redirect_to user_path(terms['id'])
else
@users = @users.search_by_name(terms.query) unless terms.query.blank?
@users = @users.with_role(terms['role']) if terms['role']
@users = @users.registered(false) if terms['guest']
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment