Skip to content

Instantly share code, notes, and snippets.

@karmi
Created February 6, 2011 14:17
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save karmi/813387 to your computer and use it in GitHub Desktop.
Save karmi/813387 to your computer and use it in GitHub Desktop.
Sketch of a Ruby API for ElasticSearch [http://elasticsearch.com]
# Sketch of a Ruby API for ElasticSearch [http://elasticsearch.com]
require 'rubygems'
require 'curb'
require 'rest_client'
require 'yajl/json_gem'
module Slingshot
def http
@http ||= Client.new
end; module_function :http
def search(indices, &block)
@search = Search.new(indices, &block)
@results = Results.new(@search)
end
def index(name, &block)
@index = Index.new(name, &block)
end
def mapping
raise NoMethodError, "TODO"
end
class Client
def post(url, data)
# Curl::Easy.http_post( url, data).body_str
RestClient.post url, data
end
def delete(url)
# Curl::Easy.http_delete(url).body_str rescue nil
RestClient.delete url rescue nil
end
end
class Results
include Enumerable
attr_reader :query, :curl, :time, :total, :results, :facets
def initialize(search)
response = JSON.parse( Slingshot.http.post("http://localhost:9200/#{search.indices}/_search", search.to_json) )
@query = search.to_json
@curl = %Q|curl -X POST "http://localhost:9200/#{search.indices}/_search?pretty=true" -d '#{@query}'|
@time = response['took']
@total = response['hits']['total']
@results = response['hits']['hits']
@facets = response['facets']
end
def each(&block)
@results.each(&block)
end
end
class Search
attr_reader :indices
def initialize(indices, &block)
@indices = indices
instance_eval(&block)
end
def query(&block)
@query = Query.new.instance_eval(&block)
end
def from(value)
@from = value
end
def size(value)
@size = value
end
def sort(&block)
@sort = Sort.new(&block)
end
def fields(fields=[])
@fields = fields
end
def facets(name, options={}, &block)
@facets = Facets.new(name, options, &block)
end
def to_json
request = { :query => @query }
request.update( { :sort => @sort } ) if @sort
request.update( { :fields => @fields } ) if @fields
request.update( { :size => @size } ) if @size
request.update( { :from => @from } ) if @from
request.update( { :facets => @facets } ) if @facets
request.to_json
end
end
class Query
def term(field, value)
@value = { :term => { field => value } }
end
def terms(field, value, options={})
@value = { :terms => { field => value } }
@value[:terms].update( { :minimum_match => options[:minimum_match] } ) if options[:minimum_match]
@value
end
def query(value, options={})
@value = { :query_string => { :query => value } }
@value[:query_string].update( { :default_field => options[:default_field] } ) if options[:default_field]
# TODO: https://github.com/elasticsearch/elasticsearch/wiki/Query-String-Query
@value
end
def to_json
@value.to_json
end
end
class Sort
def initialize(&block)
@value = []
self.instance_eval(&block) if block_given?
end
def method_missing(id, *args, &block)
case arg = args.shift
when String, Symbol, Hash then @value << { id => arg }
else @value << id
end
end
def to_json
@value.to_json
end
end
class Facets
def initialize(name, options={}, &block)
@name = name
@options = options
self.instance_eval(&block) if block_given?
end
def terms(field, options={})
@value = { :terms => { :field => field } }
end
def to_json
@value.update( @options ) if @options
request = { @name => @value }
request.to_json
end
end
class Index
def initialize(name, &block)
@name = name
instance_eval(&block)
end
def delete
Slingshot.http.delete "http://localhost:9200/#{@name}"
end
def create
Slingshot.http.post "http://localhost:9200/#{@name}", ''
end
def store(*args)
if args.size > 1
(type, document = args)
else
(document = args.pop; type = :document)
end
document = case true
when document.is_a?(String) then document
when document.respond_to?(:to_json) then document.to_json
else raise ArgumentError, "Please pass a JSON string or object with a 'to_json' method"
end
result = Slingshot.http.post "http://localhost:9200/#{@name}/#{type}/", document
JSON.parse(result)
end
def refresh
Slingshot.http.post "http://localhost:9200/#{@name}/_refresh", ''
end
end
end
# --- Usage -------------------------------------------------------------------
extend Slingshot
index 'articles' do
delete
create
store 'ruby-article', :title => 'One', :tags => ['ruby']
store 'ruby-and-python-article', :title => 'Two', :tags => ['ruby', 'python']
store :title => 'Three', :tags => ['java']
store :title => 'Four', :tags => ['ruby', 'php']
a = store '{"title" : "Five", "tags" : ["erlang"]}'
# puts "Stored last article with ID: " + a['_id'], ""
refresh
end
r = search 'articles' do
query do
# term :title, 'one'
# terms :tags, ['ruby', 'python'], :minimum_match => 2
# query 'On*', :default_field => 'title'
# terms :tags, ['ruby', 'python']
# query '*'
term :tags, 'ruby'
end
sort do
title 'desc'
_score
end
facets 'tags', :global => true do
terms :tags
end
fields :title
size 1
from 1
end
puts "Query:" , "-"*80, r.query, ""
puts "Curl:" , "-"*80, r.curl, ""
puts "Results (total: #{r.total}, query returned: #{r.count}):", "-"*80, r.inspect, ""
puts "Output:" , "-"*80
r.each_with_index do |document, i|
puts "#{i+1}. #{ document['fields']['title'] } (#{document['_type']}, #{document['_id']})"
end
puts "", "Facets:" , "-"*80
r.facets['tags']['terms'].each do |f|
puts "#{f['term'].ljust(10)} #{f['count']}"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment