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 ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ test.rb
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rake'
4
+ # Specify your gem's dependencies in khoj.gemspec
5
+ gemspec
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ About Khoj
2
+ ===========
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ task :default => :test
6
+ Rake::TestTask.new(:test) do |test|
7
+ test.libs << 'lib' <<'test'
8
+ test.pattern = 'test/*_test.rb'
9
+ test.verbose = true
10
+ end
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
+
@@ -0,0 +1,5 @@
1
+ Khoj.config do |c|
2
+ c.api_key = <%= api_key %>
3
+ c.api_host = 'http://test.search.com'
4
+ end
5
+
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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Khoj
2
+ VERSION = "0.0.1"
3
+ end
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ class ConfigurationTest < ActiveSupport::TestCase
4
+ def setup
5
+ end
6
+
7
+ def teardown
8
+ end
9
+ end
@@ -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
@@ -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
@@ -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