Skip to content

Instantly share code, notes, and snippets.

@karmi karmi/slingshot.rb
Created Feb 6, 2011

Embed
What would you like to do?
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
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.