khoj 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/Gemfile +5 -0
- data/README.md +2 -0
- data/Rakefile +10 -0
- data/khoj.gemspec +23 -0
- data/lib/generators/khoj/khoj_generator.rb +20 -0
- data/lib/generators/khoj/templates/initializer.rb +5 -0
- data/lib/khoj.rb +49 -0
- data/lib/khoj/client.rb +142 -0
- data/lib/khoj/configuration.rb +32 -0
- data/lib/khoj/function.rb +35 -0
- data/lib/khoj/index.rb +28 -0
- data/lib/khoj/version.rb +3 -0
- data/test/client_test.rb +246 -0
- data/test/configuration_test.rb +9 -0
- data/test/facet_test.rb +64 -0
- data/test/function_test.rb +24 -0
- data/test/index_test.rb +68 -0
- data/test/khoj_test.rb +64 -0
- data/test/sort_test.rb +55 -0
- data/test/test_helper.rb +11 -0
- metadata +121 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
data/khoj.gemspec
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require File.expand_path('../lib/khoj/version', __FILE__)
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |gem|
|
|
5
|
+
gem.authors = ["Jiren", "Siva", 'Swapnil', 'Pratik']
|
|
6
|
+
gem.email = ["jiren@joshsoftware.com,siva@joshsoftware.com,
|
|
7
|
+
swapnil@joshsoftware.com,pratik@joshsoftware.com"]
|
|
8
|
+
gem.description = %q{Elastic search client}
|
|
9
|
+
gem.summary = %q{Elastic search client}
|
|
10
|
+
gem.homepage = ""
|
|
11
|
+
|
|
12
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
13
|
+
gem.files = `git ls-files`.split("\n")
|
|
14
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
15
|
+
gem.name = "khoj"
|
|
16
|
+
gem.require_paths = ["lib"]
|
|
17
|
+
gem.version = Khoj::VERSION
|
|
18
|
+
gem.add_dependency('json', '>= 1.5.3')
|
|
19
|
+
gem.add_dependency("httparty", ">= 0.7.8")
|
|
20
|
+
|
|
21
|
+
gem.add_development_dependency('activesupport', '>= 3.0.0')
|
|
22
|
+
gem.add_development_dependency('i18n')
|
|
23
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class KhojGenerator < Rails::Generators::Base
|
|
2
|
+
|
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
4
|
+
class_option :api_key, :aliases => '-k', :type => :string, :desc => 'API key.'
|
|
5
|
+
|
|
6
|
+
def add_config
|
|
7
|
+
if options[:api_key]
|
|
8
|
+
template 'initializer.rb', 'config/initializers/khoj.rb'
|
|
9
|
+
else
|
|
10
|
+
p 'Set option --api-key or -k.'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
def api_key
|
|
16
|
+
"'#{options[:api_key]}'"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
data/lib/khoj.rb
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'httparty'
|
|
3
|
+
|
|
4
|
+
require 'khoj/version'
|
|
5
|
+
require 'khoj/configuration'
|
|
6
|
+
require 'khoj/index'
|
|
7
|
+
require 'khoj/client'
|
|
8
|
+
require 'khoj/function'
|
|
9
|
+
|
|
10
|
+
module Khoj
|
|
11
|
+
|
|
12
|
+
class KhojException < Exception
|
|
13
|
+
def initialize(message)
|
|
14
|
+
message = message['error'] if message.class == Hash
|
|
15
|
+
super "[KHOJ] #{message}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.config(&block)
|
|
20
|
+
yield Configuration
|
|
21
|
+
|
|
22
|
+
unless Configuration.api_host
|
|
23
|
+
Configuration.api_host = Configuration::DEFAULTS[:api_host]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if Configuration.api_key.nil? or Configuration.api_key.strip.empty?
|
|
27
|
+
Configuration.valid = false
|
|
28
|
+
raise KhojException.new('api key is nil.')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Configuration.valid = true
|
|
32
|
+
#Configuration.freeze
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
@@clients = {}
|
|
36
|
+
|
|
37
|
+
def self.client(index)
|
|
38
|
+
@@clients[index] ||= Client.new(index)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@@functions = {}
|
|
42
|
+
|
|
43
|
+
def self.function(index)
|
|
44
|
+
@@functions[index] ||= Function.new(index)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
end
|
data/lib/khoj/client.rb
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
module Khoj
|
|
2
|
+
class Client
|
|
3
|
+
include Index
|
|
4
|
+
|
|
5
|
+
DEFAULT_DOC_TYPE = 'default'
|
|
6
|
+
DEFAULT_DOC_FIELD = 'text'
|
|
7
|
+
DEFAULT_SEARCH_LIMIT = 10
|
|
8
|
+
DEFAULT_ORDER = 'asc'
|
|
9
|
+
|
|
10
|
+
attr_accessor :index
|
|
11
|
+
attr_reader :_index
|
|
12
|
+
|
|
13
|
+
#alias_method :update_categories, :add
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def initialize(index)
|
|
17
|
+
@index = index
|
|
18
|
+
@_index = "#{Configuration.api_key}-#{index}"
|
|
19
|
+
@conn = Configuration.connection
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get(resource_id, options = {})
|
|
23
|
+
response = @conn.get("/#{_index}/#{resource_type(resource_id).join('/')}")
|
|
24
|
+
response.parsed_response
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def add(resource_id, options = {})
|
|
28
|
+
resource_name, id = resource_type(resource_id)
|
|
29
|
+
resource = if options.class == String
|
|
30
|
+
return if options.strip.empty?
|
|
31
|
+
{ DEFAULT_DOC_FIELD => options}
|
|
32
|
+
else
|
|
33
|
+
return if options.empty?
|
|
34
|
+
options
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
req_options = {:body => resource.to_json, :format => 'json'}
|
|
38
|
+
response = @conn.post("/#{_index}/#{resource_name}/#{id}", req_options)
|
|
39
|
+
|
|
40
|
+
case response.code
|
|
41
|
+
when 200
|
|
42
|
+
true
|
|
43
|
+
when 201
|
|
44
|
+
true
|
|
45
|
+
else
|
|
46
|
+
raise KhojException.new(response.parsed_response)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def delete(resource_id)
|
|
51
|
+
id, resource_name = resource_id.to_s.split(':').reverse
|
|
52
|
+
resource_type = resource_name == nil ? [id] : [resource_name || DEFAULT_DOC_TYPE, id]
|
|
53
|
+
response = @conn.delete("/#{_index}/#{resource_type(resource_id).join('/')}")
|
|
54
|
+
response.code == 200
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def search(query, options = {})
|
|
58
|
+
search_uri = options[:type] ? "#{options[:type]}/_search?pretty=true" : '_search?pretty=true'
|
|
59
|
+
|
|
60
|
+
# check that if search string contains AND, OR or NOT in query
|
|
61
|
+
# if it is then we will execute String query of Query DSL
|
|
62
|
+
# else we will execute normal search query
|
|
63
|
+
if query.scan(/\sAND|NOT|OR+\s/).empty?
|
|
64
|
+
|
|
65
|
+
options[:field] ||= DEFAULT_DOC_FIELD
|
|
66
|
+
q = {:query =>
|
|
67
|
+
{:term => { options[:field] => query}}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#q[:query][:fields] ||= ['_id' , '_type']
|
|
71
|
+
q[:size] ||= (options[:size] ? (options[:size] > 10 ? DEFAULT_SEARCH_LIMIT : options[:size]) : DEFAULT_SEARCH_LIMIT) if options[:size]
|
|
72
|
+
q[:query] = q[:query].merge(:script_fields => { "#{options[:fetch]}" => { :script => "_source.#{options[:fetch]}"}}) if options[:fetch]
|
|
73
|
+
else
|
|
74
|
+
|
|
75
|
+
# TODO : implement functionality for fetch action if specified by user to fetch fields from result set
|
|
76
|
+
q = get_string_query(query, options)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
if category_filter = options[:category_filter]
|
|
80
|
+
q[:facets] = {}
|
|
81
|
+
q[:filter] = {:term => {}}
|
|
82
|
+
category_filter.keys.each do |key|
|
|
83
|
+
q[:facets][key.to_s] = {:terms => {:field => key.to_s}, :facet_filter => {:term => {key => category_filter[key]}}}
|
|
84
|
+
q[:filter][:term].merge!({key => category_filter[key]})
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
#Check if sorting function is specified in the query.
|
|
89
|
+
#index.search 'test', :function => {:cordinates => "00,00", :order => 'desc'}
|
|
90
|
+
# "sort" => {:geo_location => {:cordinates => ["xx,yy"]}, :fields => {:price => 'xxx', :tags => 'xxx'}}
|
|
91
|
+
if sort = options[:sort]
|
|
92
|
+
q["sort"] = []
|
|
93
|
+
sort.keys.each do |key|
|
|
94
|
+
if key == :geo_location
|
|
95
|
+
q["sort"] << { "_geo_distance" => { "location" => "#{sort[:geo_location][:cordinates]}", "order" => "#{sort[:geo_location][:order] || DEFAULT_ORDER}", "unit" => "km" }}
|
|
96
|
+
else
|
|
97
|
+
q["sort"] << sort[:fields]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
response = @conn.get("/#{_index}/#{search_uri}", :body => q.to_json)
|
|
103
|
+
|
|
104
|
+
case response.code
|
|
105
|
+
when 200
|
|
106
|
+
response.parsed_response
|
|
107
|
+
else
|
|
108
|
+
nil
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
def resource_type(resource_id)
|
|
114
|
+
id, resource_name = resource_id.to_s.split(':').reverse
|
|
115
|
+
return [resource_name || DEFAULT_DOC_TYPE, id]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def get_string_query(query, options)
|
|
119
|
+
q = {:query =>
|
|
120
|
+
{:query_string => {
|
|
121
|
+
:query => query
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# while using string query default field in elastic search is _all
|
|
127
|
+
# if user specify fields then
|
|
128
|
+
# if fields contains only one 1 field/word then pass it as 'default_field' parameter
|
|
129
|
+
# if fields has more than 1 fields/words then pass them as 'fields' parameter
|
|
130
|
+
fields = options[:fields].split(',').each do |i| i.strip! end if options[:fields]
|
|
131
|
+
if fields
|
|
132
|
+
if fields.size == 1
|
|
133
|
+
q[:query][:query_string] = q[:query][:query_string].merge(:default_field => fields[0])
|
|
134
|
+
elsif fields.size > 1
|
|
135
|
+
q[:query][:query_string] = q[:query][:query_string].merge(:fields => fields)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
return q
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Khoj
|
|
2
|
+
class Configuration
|
|
3
|
+
DEFAULTS = {
|
|
4
|
+
:api_host => 'http://localhost:9200'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
attr_accessor :api_key
|
|
9
|
+
attr_accessor :api_host
|
|
10
|
+
attr_accessor :valid
|
|
11
|
+
|
|
12
|
+
def connection
|
|
13
|
+
@connection ||= Connection.new(:url => Configuration.api_host).class
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def valid?
|
|
17
|
+
self.valid
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Connection
|
|
23
|
+
include HTTParty
|
|
24
|
+
format :json
|
|
25
|
+
|
|
26
|
+
def initialize(options = {})
|
|
27
|
+
self.class.base_uri(options[:url])
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Khoj
|
|
2
|
+
class Function
|
|
3
|
+
include Index
|
|
4
|
+
|
|
5
|
+
attr_accessor :index
|
|
6
|
+
attr_reader :_index
|
|
7
|
+
|
|
8
|
+
def initialize(index)
|
|
9
|
+
@index = index
|
|
10
|
+
@_index = "#{Configuration.api_key}-#{index}"
|
|
11
|
+
@conn = Configuration.connection
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def add(funtion_name, options ={})
|
|
15
|
+
type = options[:type]
|
|
16
|
+
if funtion_name == 'geo_location'
|
|
17
|
+
geo_mapping = { "#{type}" => { "properties" => { "location" => { "type" => "geo_point" } } } }
|
|
18
|
+
req_options = {:body => geo_mapping.to_json, :format => 'json'}
|
|
19
|
+
response = @conn.post("/#{_index}/#{type}/_mapping", req_options)
|
|
20
|
+
case response.code
|
|
21
|
+
when 200
|
|
22
|
+
true
|
|
23
|
+
when 201
|
|
24
|
+
true
|
|
25
|
+
else
|
|
26
|
+
raise KhojException.new(response.parsed_response)
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
raise KhojException.new('No function found with given name')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
data/lib/khoj/index.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Khoj
|
|
2
|
+
module Index
|
|
3
|
+
|
|
4
|
+
def create_index
|
|
5
|
+
response = @conn.put("/#{_index}")
|
|
6
|
+
response.code == 200 ? true : (raise KhojException.new(response.parsed_response))
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def delete_index
|
|
10
|
+
response = @conn.delete("/#{_index}")
|
|
11
|
+
response.code == 200 ? true : (raise KhojException.new(response.parsed_response))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def index?
|
|
15
|
+
@conn.head("/#{_index}").code == 200 ? true : false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def index_stats
|
|
19
|
+
response = @conn.get("/#{_index }/_stats")
|
|
20
|
+
if response.code == 200
|
|
21
|
+
response.parsed_response['_all']['total']['docs']['count']
|
|
22
|
+
else
|
|
23
|
+
raise KhojException.new(response.parsed_response)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/khoj/version.rb
ADDED
data/test/client_test.rb
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class ClientTest < ActiveSupport::TestCase
|
|
4
|
+
def setup
|
|
5
|
+
@api_key = 'test-api-key'
|
|
6
|
+
@index = 'test-client'
|
|
7
|
+
|
|
8
|
+
Khoj.config do |c|
|
|
9
|
+
c.api_key = @api_key
|
|
10
|
+
c.api_host = Khoj::Configuration::DEFAULTS[:api_host]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
@client = Khoj.client(@index)
|
|
14
|
+
@document = {:test => 'from the the test case'}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def teardown
|
|
18
|
+
@client.delete('1') rescue ''
|
|
19
|
+
@client.delete("test") rescue ''
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
test 'should be able to add document' do
|
|
23
|
+
response = @client.add('test:1', @document)
|
|
24
|
+
sleep(1)
|
|
25
|
+
assert_equal true, response
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
test 'should be able to add document to default document type' do
|
|
29
|
+
response = @client.add('1', @document) # like doc id 'default:1'
|
|
30
|
+
sleep(1)
|
|
31
|
+
assert_equal true, response
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
test 'should be able to add document with default text field if string is input' do
|
|
35
|
+
response = @client.add('1', 'test for default text field')
|
|
36
|
+
sleep(1)
|
|
37
|
+
assert_equal true, response
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
test 'should be able to retrive docuemt' do
|
|
41
|
+
@client.add('test:1', @document)
|
|
42
|
+
sleep(1)
|
|
43
|
+
response = @client.get('test:1')
|
|
44
|
+
|
|
45
|
+
assert_equal true, response['exists']
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# default:1 : type => default, id => 1
|
|
49
|
+
test 'should be able to retrive default type document' do
|
|
50
|
+
@client.add('1', @document)
|
|
51
|
+
sleep(1)
|
|
52
|
+
response = @client.get('1')
|
|
53
|
+
|
|
54
|
+
assert_equal true, response['exists']
|
|
55
|
+
assert_equal Khoj::Client::DEFAULT_DOC_TYPE, response['_type']
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
test 'should be able to delete document' do
|
|
59
|
+
@client.add('test:1', @document)
|
|
60
|
+
sleep(1)
|
|
61
|
+
response = @client.delete('test:1')
|
|
62
|
+
assert_equal true, response
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
test 'should be able to delete default type document' do
|
|
66
|
+
@client.add('1', @document)
|
|
67
|
+
sleep(1)
|
|
68
|
+
response = @client.delete('1')
|
|
69
|
+
assert_equal true, response
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
#Default doc type : 'default', Default filed : text
|
|
73
|
+
test 'should be able to search document using default type and field' do
|
|
74
|
+
@client.delete(100)
|
|
75
|
+
@client.add(100, 'xxx yyy zzz')
|
|
76
|
+
sleep(2)
|
|
77
|
+
|
|
78
|
+
response = @client.search('xxx')
|
|
79
|
+
|
|
80
|
+
assert_equal 1, response['hits']['total']
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
test 'should be able to search document using field name' do
|
|
84
|
+
@client.add('test:2', {:test => 'test2'})
|
|
85
|
+
sleep(1)
|
|
86
|
+
|
|
87
|
+
response = @client.search('test2', {:field => 'test'})
|
|
88
|
+
assert_equal 1, response['hits']['total']
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
test 'should be able to search document by type' do
|
|
92
|
+
@client.add('test:3', {:test => 'test3'})
|
|
93
|
+
sleep(1)
|
|
94
|
+
|
|
95
|
+
response = @client.search('test3', {:field => 'test', :type => 'test'})
|
|
96
|
+
assert_equal 1, response['hits']['total']
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
#size
|
|
100
|
+
test 'should be able to search number of records as size is specified, 10 by default' do
|
|
101
|
+
15.times do |i|
|
|
102
|
+
@client.add("test:#{i+1}", 'test application')
|
|
103
|
+
end
|
|
104
|
+
sleep(1)
|
|
105
|
+
response = @client.search('test', :size => 0)
|
|
106
|
+
assert_equal 0, response['hits']['hits'].count
|
|
107
|
+
|
|
108
|
+
response = @client.search('test', :size => 5)
|
|
109
|
+
assert_equal 5, response['hits']['hits'].count
|
|
110
|
+
|
|
111
|
+
response = @client.search('test')
|
|
112
|
+
assert_equal 10, response['hits']['hits'].count
|
|
113
|
+
|
|
114
|
+
response = @client.search('test', :size => 15)
|
|
115
|
+
assert_equal 10, response['hits']['hits'].count
|
|
116
|
+
|
|
117
|
+
6.times do |i|
|
|
118
|
+
@client.delete("test:#{i+1}") rescue ''
|
|
119
|
+
end
|
|
120
|
+
sleep(1)
|
|
121
|
+
|
|
122
|
+
response = @client.search('test')
|
|
123
|
+
assert_equal 9, response['hits']['hits'].count
|
|
124
|
+
|
|
125
|
+
response = @client.search('test', :size => 10)
|
|
126
|
+
assert_equal 9, response['hits']['hits'].count
|
|
127
|
+
|
|
128
|
+
15.times do |i|
|
|
129
|
+
@client.delete("test:#{i+1}") rescue ''
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
test 'should fetch specified field from documents from which search match is found, if not present then should return nil' do
|
|
134
|
+
field = 'designation'
|
|
135
|
+
designation = 'Software Engineer'
|
|
136
|
+
|
|
137
|
+
# matching text 'software' with designation
|
|
138
|
+
@client.add('test:1', :text => 'I am a software engineer', :city => 'Pune', :designation => designation)
|
|
139
|
+
|
|
140
|
+
# matching text without designation
|
|
141
|
+
@client.add('test:2', :text => 'I am a freelancer who developes software', :city => 'Mumbai')
|
|
142
|
+
|
|
143
|
+
#unmatching text with designation
|
|
144
|
+
@client.add('test:3', :text => 'I do marketing of products', :city => 'Delhi', :designation => 'Marketing Executive')
|
|
145
|
+
sleep(1)
|
|
146
|
+
|
|
147
|
+
response = @client.search('software', :fetch=> field)
|
|
148
|
+
|
|
149
|
+
ids = [] # ids array will contain ids of documents
|
|
150
|
+
fields = [] # fields array will contain fetched fields, each field is in key => value format such as "fields"=>{"designation"=>"Software Engineer"}
|
|
151
|
+
field_values = [] # fields_values will contain values of fetched fields such as 'Software Engineer','Marketing Executive',nil etc.
|
|
152
|
+
|
|
153
|
+
# Data received in json format is,
|
|
154
|
+
# {
|
|
155
|
+
# "took"=>5, "timed_out"=>false, "_shards"=>{"total"=>5, "successful"=>5, "failed"=>0},
|
|
156
|
+
# "hits"=>{"total"=>2, "max_score"=>0.15342641,
|
|
157
|
+
# "hits"=>[
|
|
158
|
+
# {"_index"=>"test-api-key-test", "_type"=>"test", "_id"=>"1", "_score"=>0.15342641, "fields"=>{"designation"=>"Software Engineer"}},
|
|
159
|
+
# {"_index"=>"test-api-key-test", "_type"=>"test", "_id"=>"2", "_score"=>0.11506981, "fields"=>{"designation"=>nil}}
|
|
160
|
+
# ]
|
|
161
|
+
# }
|
|
162
|
+
# }
|
|
163
|
+
|
|
164
|
+
response['hits']['hits'].each do |i|
|
|
165
|
+
ids << i['_id']
|
|
166
|
+
fields << i['fields']
|
|
167
|
+
field_values << i['fields'].values
|
|
168
|
+
end
|
|
169
|
+
field_values.flatten!
|
|
170
|
+
# as per data entered, hits should be 2 as word 'software' is in only 2 documents
|
|
171
|
+
assert_equal 2, response['hits']['total']
|
|
172
|
+
|
|
173
|
+
# fields array should contain uniq element as only one field is fetched
|
|
174
|
+
assert_equal 1, fields.collect(&:keys).flatten.uniq.count
|
|
175
|
+
|
|
176
|
+
# key of every element in fields array should be 'designation'
|
|
177
|
+
assert_equal field, fields.collect(&:keys).flatten.uniq.first
|
|
178
|
+
|
|
179
|
+
# ids array should contain 1 and 2, not 3
|
|
180
|
+
assert_equal true, ids.include?('1')
|
|
181
|
+
assert_equal true, ids.include?('2')
|
|
182
|
+
assert_equal false, ids.include?('3')
|
|
183
|
+
|
|
184
|
+
# Designation value should be
|
|
185
|
+
# software enginner for document test:1 and nil for test:2
|
|
186
|
+
# it should not contain value as Marketing Executive
|
|
187
|
+
assert_equal true, field_values.include?(designation)
|
|
188
|
+
assert_equal true, field_values.include?(nil)
|
|
189
|
+
assert_equal false, field_values.include?('Marketing Executive')
|
|
190
|
+
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
test 'should perofrm search operation using operators[AND/OR/NOT], when no field specified, default is all fields' do
|
|
194
|
+
add_data_with_multiple_fields
|
|
195
|
+
|
|
196
|
+
# Search data with operators using no fields specified
|
|
197
|
+
response = @client.search('marketing OR tester')
|
|
198
|
+
ids = get_ids_from_response(response)
|
|
199
|
+
assert_equal 2, ids.size
|
|
200
|
+
assert_equal true, ids.include?('3')
|
|
201
|
+
assert_equal true, ids.include?('4')
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
test 'should perofrm operations using operators, when single field is specified' do
|
|
205
|
+
add_data_with_multiple_fields
|
|
206
|
+
# Search data with operators and within single field
|
|
207
|
+
response = @client.search('delhi OR mumbai', :fields => "city")
|
|
208
|
+
ids = get_ids_from_response(response)
|
|
209
|
+
assert_equal 2, response['hits']['total']
|
|
210
|
+
assert_equal true, ids.include?('2')
|
|
211
|
+
assert_equal true, ids.include?('3')
|
|
212
|
+
|
|
213
|
+
response = @client.search('tester OR pune', :fields => "text")
|
|
214
|
+
assert_equal 0, response['hits']['total']
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
test 'should perofrm operations using operators, when multiple fields are specified' do
|
|
218
|
+
add_data_with_multiple_fields
|
|
219
|
+
# Search data with operators and with multiple fields
|
|
220
|
+
response = @client.search('software AND mumbai', :fields => "designation, city")
|
|
221
|
+
assert_equal 1, response['hits']['total']
|
|
222
|
+
assert_equal '2', response['hits']['hits'].first['_id']
|
|
223
|
+
|
|
224
|
+
response = @client.search('software OR pune NOT tester NOT manager NOT mumbai', :fields => "designation, city")
|
|
225
|
+
assert_equal 1, response['hits']['total']
|
|
226
|
+
assert_equal '1', response['hits']['hits'].first['_id']
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def add_data_with_multiple_fields
|
|
230
|
+
# While changig data in this method make sure to do changed in all respective places as the data fields are used for validation of test cases
|
|
231
|
+
@client.add('test:1', :text => 'I am a software engineer', :city => 'Pune', :designation => 'Software engineer')
|
|
232
|
+
@client.add('test:2', :text => 'I am a freelancer who developes software', :city => 'Mumbai', :designation => 'Senior Software Engineer')
|
|
233
|
+
@client.add('test:3', :text => 'I do marketing of products', :city => 'Delhi', :designation => 'Marketing Executive')
|
|
234
|
+
@client.add('test:4', :text => 'I test the product', :city => 'Pune, University Road', :designation => 'Tester')
|
|
235
|
+
@client.add('test:5', :text => 'I manage the company', :city => 'Pune, Shivajinagar', :designation => 'Manager')
|
|
236
|
+
sleep(1)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def get_ids_from_response(response)
|
|
240
|
+
ids = []
|
|
241
|
+
response['hits']['hits'].each do |i|
|
|
242
|
+
ids << i['_id']
|
|
243
|
+
end
|
|
244
|
+
return ids
|
|
245
|
+
end
|
|
246
|
+
end
|
data/test/facet_test.rb
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class FacetTest < ActiveSupport::TestCase
|
|
4
|
+
def setup
|
|
5
|
+
@api_key = 'test-api-key'
|
|
6
|
+
@index = 'test-facet'
|
|
7
|
+
|
|
8
|
+
Khoj.config do |c|
|
|
9
|
+
c.api_key = @api_key
|
|
10
|
+
c.api_host = Khoj::Configuration::DEFAULTS[:api_host]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
@client = Khoj.client(@index)
|
|
14
|
+
@document = {:test => 'from the the test case'}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def teardown
|
|
18
|
+
@client.delete('test:1') rescue ''
|
|
19
|
+
@client.delete('test:2') rescue ''
|
|
20
|
+
@client.delete('test:3') rescue ''
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test 'should be able to filter facet count' do
|
|
24
|
+
@client.add('test:1', {:test => 'test1', :tags => ['foo']})
|
|
25
|
+
@client.add('test:2', {:test => 'test2', :tags => ['baz']})
|
|
26
|
+
sleep(5)
|
|
27
|
+
response = @client.search('test1', {:field => 'test', :type => 'test', :category_filter =>{'tags' => ['foo']}})
|
|
28
|
+
assert_equal 1, response['facets']['tags']['total']
|
|
29
|
+
assert_equal 1, response['hits']['total']
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test 'facet count depends on search result' do
|
|
33
|
+
@client.add('test:1', {:test => 'test1', :tags => ['foo']})
|
|
34
|
+
@client.add('test:2', {:test => 'test2', :tags => ['foo']})
|
|
35
|
+
sleep(5)
|
|
36
|
+
|
|
37
|
+
response = @client.search('test1', {:field => 'test', :type => 'test', :category_filter =>{'tags' => ['foo']}})
|
|
38
|
+
|
|
39
|
+
assert_equal 1, response['facets']['tags']['total']
|
|
40
|
+
assert_equal 1, response['hits']['total']
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
test 'should be able to filter multiple facet count' do
|
|
44
|
+
@client.add('test:3', {:test => 'test3', :tags => ['foo'], :location => 'london'})
|
|
45
|
+
sleep(5)
|
|
46
|
+
|
|
47
|
+
response = @client.search('test3', {:field => 'test', :type => 'test', :category_filter =>{'tags' => ['foo'], 'location' => 'london'}})
|
|
48
|
+
|
|
49
|
+
assert_equal 1, response['facets']['tags']['total']
|
|
50
|
+
assert_equal 1, response['facets']['location']['total']
|
|
51
|
+
assert_equal 1, response['hits']['total']
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
test 'should perform AND operation on multiple value passed for single field' do
|
|
55
|
+
@client.add('test:1', {:test => 'test1', :tags => ['foo'], :location => ['pune', 'mumbai', 'delhi']})
|
|
56
|
+
@client.add('test:2', {:test => 'test2', :tags => ['foo'], :location => ['pune', 'mumbai']})
|
|
57
|
+
sleep(5)
|
|
58
|
+
|
|
59
|
+
response = @client.search('test1', {:field => 'test', :type => 'test', :category_filter =>{'location' => ['pune', 'mumbai', 'delhi']}})
|
|
60
|
+
assert_equal 3, response['facets']['location']['total']
|
|
61
|
+
assert_equal 1, response['hits']['total']
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class FunctionTest < ActiveSupport::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@api_key = 'test-api-key'
|
|
7
|
+
@index = 'test'
|
|
8
|
+
|
|
9
|
+
Khoj.config do |c|
|
|
10
|
+
c.api_key = @api_key
|
|
11
|
+
c.api_host = Khoj::Configuration::DEFAULTS[:api_host]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@client= Khoj.client(@index)
|
|
15
|
+
@function = Khoj.function(@index)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
test 'should be able to map document field type to geo_type' do
|
|
19
|
+
response = @function.add('geo_location', :type => 'function_test')
|
|
20
|
+
sleep(1)
|
|
21
|
+
assert_equal true, response
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
data/test/index_test.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class IndexTest < ActiveSupport::TestCase
|
|
4
|
+
def setup
|
|
5
|
+
@api_key = 'test-api-key'
|
|
6
|
+
@index = 'test'
|
|
7
|
+
|
|
8
|
+
Khoj.config do |c|
|
|
9
|
+
c.api_key = @api_key
|
|
10
|
+
c.api_host = Khoj::Configuration::DEFAULTS[:api_host]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def teardown
|
|
15
|
+
begin
|
|
16
|
+
client = Khoj.client(@index)
|
|
17
|
+
client.delete_index
|
|
18
|
+
rescue Exception => e
|
|
19
|
+
""
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test 'should create index. check index exist or not and delete index' do
|
|
24
|
+
@index = "test_1"
|
|
25
|
+
client = Khoj.client(@index)
|
|
26
|
+
|
|
27
|
+
assert_equal true, client.create_index
|
|
28
|
+
assert_equal true, client.index?
|
|
29
|
+
assert_equal true, client.delete_index
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test 'should throw exception on create if index already exist' do
|
|
33
|
+
@index = "test_2"
|
|
34
|
+
client = Khoj.client(@index)
|
|
35
|
+
client.create_index
|
|
36
|
+
|
|
37
|
+
assert_raise Khoj::KhojException do
|
|
38
|
+
client.create_index
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
test 'should throw exception on delete if index not exist' do
|
|
43
|
+
@index = "test_3"
|
|
44
|
+
client = Khoj.client(@index)
|
|
45
|
+
|
|
46
|
+
assert_raise Khoj::KhojException do
|
|
47
|
+
client.delete_index
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test 'should return index state' do
|
|
52
|
+
@index = "test_4"
|
|
53
|
+
client = Khoj.client(@index)
|
|
54
|
+
client.create_index
|
|
55
|
+
|
|
56
|
+
assert_equal 0, client.index_stats
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
test 'index name should be in lower case' do
|
|
60
|
+
@index = "TEST-INDEX"
|
|
61
|
+
client = Khoj.client(@index)
|
|
62
|
+
|
|
63
|
+
assert_raise Khoj::KhojException do
|
|
64
|
+
client.create_index
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
data/test/khoj_test.rb
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class KhojTest < ActiveSupport::TestCase
|
|
5
|
+
def setup
|
|
6
|
+
@api_key = 'API-KEY'
|
|
7
|
+
@api_host = 'http://testclient.com'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def teardown
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
test 'should init configuration' do
|
|
14
|
+
Khoj.config do |c|
|
|
15
|
+
c.api_key = @api_key
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
assert_equal @api_key, Khoj::Configuration.api_key
|
|
19
|
+
assert_equal Khoj::Configuration::DEFAULTS[:api_host], Khoj::Configuration.api_host
|
|
20
|
+
assert_equal true, Khoj::Configuration.valid?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test 'for nil or empty api key config should not be valid' do
|
|
24
|
+
assert_raise Khoj::KhojException do
|
|
25
|
+
Khoj.config do |c|
|
|
26
|
+
c.api_key = nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
assert_raise Khoj::KhojException do
|
|
31
|
+
Khoj.config do |c|
|
|
32
|
+
c.api_key = " "
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
test 'should set api host' do
|
|
38
|
+
Khoj.config do |c|
|
|
39
|
+
c.api_key = @api_key
|
|
40
|
+
c.api_host = @api_host
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
assert_equal @api_host, Khoj::Configuration.api_host
|
|
44
|
+
|
|
45
|
+
Khoj::Configuration.api_host = Khoj::Configuration::DEFAULTS[:api_host]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
test 'should create api client' do
|
|
50
|
+
api_key = 'test-api-key'
|
|
51
|
+
index = 'test'
|
|
52
|
+
Khoj.config do |c|
|
|
53
|
+
c.api_key = api_key
|
|
54
|
+
c.api_host = Khoj::Configuration::DEFAULTS[:api_host]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
client = Khoj.client(index)
|
|
58
|
+
|
|
59
|
+
assert_equal 'test', client.index
|
|
60
|
+
assert_equal "#{api_key}-#{index}", client._index
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
end
|
data/test/sort_test.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class SortTest < ActiveSupport::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@api_key = 'test-api-key-new'
|
|
7
|
+
@index = 'test-new'
|
|
8
|
+
|
|
9
|
+
Khoj.config do |c|
|
|
10
|
+
c.api_key = @api_key
|
|
11
|
+
c.api_host = Khoj::Configuration::DEFAULTS[:api_host]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@client= Khoj.client(@index)
|
|
15
|
+
@function = Khoj.function(@index)
|
|
16
|
+
@function.add('geo_location', :type => 'sort_test')
|
|
17
|
+
@client.add('sort_test:1', :text => 'sort_test', :location => {:lat => '100.00', :lon => "100.00"}, :price => '100' ,:range => '200')
|
|
18
|
+
@client.add('sort_test:2', :text => 'sort_test', :location => {:lat => '50.00', :lon => "50.00"}, :price => '200', :range => '100')
|
|
19
|
+
@client.add('sort_test:3', :text => 'sort_test', :location => {:lat => '00.00', :lon => "00.00"}, :price => '300', :range => '100')
|
|
20
|
+
sleep(5)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def teardown
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
test 'should be able to sort document according to location in ascending order' do
|
|
28
|
+
response = @client.search 'sort_test', :sort => {:geo_location => {:cordinates => "100,100", :order => 'asc'}}, :size => 3, :type => 'sort_test'
|
|
29
|
+
assert_equal "1", response["hits"]["hits"][0]["_id"]
|
|
30
|
+
assert_equal "3", response["hits"]["hits"][1]["_id"]
|
|
31
|
+
assert_equal "2", response["hits"]["hits"][2]["_id"]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
test 'should be able to sort document according to location in descending order' do
|
|
35
|
+
response = @client.search 'sort_test', :sort => {:geo_location => {:cordinates => "100,100", :order => 'desc'}}, :size => 3, :type => 'sort_test'
|
|
36
|
+
assert_equal "2", response["hits"]["hits"][0]["_id"]
|
|
37
|
+
assert_equal "3", response["hits"]["hits"][1]["_id"]
|
|
38
|
+
assert_equal "1", response["hits"]["hits"][2]["_id"]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
test 'should be able to sort document according price range' do
|
|
42
|
+
response = @client.search 'sort_test', :sort => {:fields => {:price => "asc"}}, :size => 3, :type => 'sort_test'
|
|
43
|
+
assert_equal "1", response["hits"]["hits"][0]["_id"]
|
|
44
|
+
assert_equal "2", response["hits"]["hits"][1]["_id"]
|
|
45
|
+
assert_equal "3", response["hits"]["hits"][2]["_id"]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
test 'should be able to sort document primarily by range and then price range' do
|
|
49
|
+
response = @client.search 'sort_test', :sort => {:fields => {:range => 'desc', :price => "desc"}}, :size => 3, :type => 'sort_test'
|
|
50
|
+
assert_equal "1", response["hits"]["hits"][0]["_id"]
|
|
51
|
+
assert_equal "3", response["hits"]["hits"][1]["_id"]
|
|
52
|
+
assert_equal "2", response["hits"]["hits"][2]["_id"]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'active_support/test_case'
|
|
3
|
+
|
|
4
|
+
unless $LOAD_PATH.include? 'lib'
|
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
6
|
+
$LOAD_PATH.unshift(File.join($LOAD_PATH.first, '..', 'lib'))
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
#NOTE: rake test TEST=test.rb
|
|
10
|
+
|
|
11
|
+
require 'khoj'
|
metadata
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: khoj
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Jiren
|
|
9
|
+
- Siva
|
|
10
|
+
- Swapnil
|
|
11
|
+
- Pratik
|
|
12
|
+
autorequire:
|
|
13
|
+
bindir: bin
|
|
14
|
+
cert_chain: []
|
|
15
|
+
date: 2012-01-11 00:00:00.000000000 Z
|
|
16
|
+
dependencies:
|
|
17
|
+
- !ruby/object:Gem::Dependency
|
|
18
|
+
name: json
|
|
19
|
+
requirement: &2162535300 !ruby/object:Gem::Requirement
|
|
20
|
+
none: false
|
|
21
|
+
requirements:
|
|
22
|
+
- - ! '>='
|
|
23
|
+
- !ruby/object:Gem::Version
|
|
24
|
+
version: 1.5.3
|
|
25
|
+
type: :runtime
|
|
26
|
+
prerelease: false
|
|
27
|
+
version_requirements: *2162535300
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: httparty
|
|
30
|
+
requirement: &2162534800 !ruby/object:Gem::Requirement
|
|
31
|
+
none: false
|
|
32
|
+
requirements:
|
|
33
|
+
- - ! '>='
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: 0.7.8
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: *2162534800
|
|
39
|
+
- !ruby/object:Gem::Dependency
|
|
40
|
+
name: activesupport
|
|
41
|
+
requirement: &2162534340 !ruby/object:Gem::Requirement
|
|
42
|
+
none: false
|
|
43
|
+
requirements:
|
|
44
|
+
- - ! '>='
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 3.0.0
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: *2162534340
|
|
50
|
+
- !ruby/object:Gem::Dependency
|
|
51
|
+
name: i18n
|
|
52
|
+
requirement: &2162533960 !ruby/object:Gem::Requirement
|
|
53
|
+
none: false
|
|
54
|
+
requirements:
|
|
55
|
+
- - ! '>='
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '0'
|
|
58
|
+
type: :development
|
|
59
|
+
prerelease: false
|
|
60
|
+
version_requirements: *2162533960
|
|
61
|
+
description: Elastic search client
|
|
62
|
+
email:
|
|
63
|
+
- ! "jiren@joshsoftware.com,siva@joshsoftware.com,\n swapnil@joshsoftware.com,pratik@joshsoftware.com"
|
|
64
|
+
executables: []
|
|
65
|
+
extensions: []
|
|
66
|
+
extra_rdoc_files: []
|
|
67
|
+
files:
|
|
68
|
+
- .gitignore
|
|
69
|
+
- Gemfile
|
|
70
|
+
- README.md
|
|
71
|
+
- Rakefile
|
|
72
|
+
- khoj.gemspec
|
|
73
|
+
- lib/generators/khoj/khoj_generator.rb
|
|
74
|
+
- lib/generators/khoj/templates/initializer.rb
|
|
75
|
+
- lib/khoj.rb
|
|
76
|
+
- lib/khoj/client.rb
|
|
77
|
+
- lib/khoj/configuration.rb
|
|
78
|
+
- lib/khoj/function.rb
|
|
79
|
+
- lib/khoj/index.rb
|
|
80
|
+
- lib/khoj/version.rb
|
|
81
|
+
- test/client_test.rb
|
|
82
|
+
- test/configuration_test.rb
|
|
83
|
+
- test/facet_test.rb
|
|
84
|
+
- test/function_test.rb
|
|
85
|
+
- test/index_test.rb
|
|
86
|
+
- test/khoj_test.rb
|
|
87
|
+
- test/sort_test.rb
|
|
88
|
+
- test/test_helper.rb
|
|
89
|
+
homepage: ''
|
|
90
|
+
licenses: []
|
|
91
|
+
post_install_message:
|
|
92
|
+
rdoc_options: []
|
|
93
|
+
require_paths:
|
|
94
|
+
- lib
|
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
96
|
+
none: false
|
|
97
|
+
requirements:
|
|
98
|
+
- - ! '>='
|
|
99
|
+
- !ruby/object:Gem::Version
|
|
100
|
+
version: '0'
|
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
|
+
none: false
|
|
103
|
+
requirements:
|
|
104
|
+
- - ! '>='
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
version: '0'
|
|
107
|
+
requirements: []
|
|
108
|
+
rubyforge_project:
|
|
109
|
+
rubygems_version: 1.8.10
|
|
110
|
+
signing_key:
|
|
111
|
+
specification_version: 3
|
|
112
|
+
summary: Elastic search client
|
|
113
|
+
test_files:
|
|
114
|
+
- test/client_test.rb
|
|
115
|
+
- test/configuration_test.rb
|
|
116
|
+
- test/facet_test.rb
|
|
117
|
+
- test/function_test.rb
|
|
118
|
+
- test/index_test.rb
|
|
119
|
+
- test/khoj_test.rb
|
|
120
|
+
- test/sort_test.rb
|
|
121
|
+
- test/test_helper.rb
|