khoj 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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