elastic-app-search 7.4.0 → 7.8.0
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.
- checksums.yaml +4 -4
- data/README.md +332 -34
- data/lib/elastic/app-search/client.rb +18 -2
- data/lib/elastic/app-search/client/analytics.rb +25 -0
- data/lib/elastic/app-search/client/click.rb +15 -0
- data/lib/elastic/app-search/client/credentials.rb +35 -0
- data/lib/elastic/app-search/client/curations.rb +35 -0
- data/lib/elastic/app-search/client/engines.rb +2 -0
- data/lib/elastic/app-search/client/logs.rb +15 -0
- data/lib/elastic/app-search/client/meta_engines.rb +23 -0
- data/lib/elastic/app-search/client/schema.rb +20 -0
- data/lib/elastic/app-search/client/synonyms.rb +35 -0
- data/lib/elastic/app-search/version.rb +1 -1
- data/spec/analytics_spec.rb +82 -0
- data/spec/click_spec.rb +30 -0
- data/spec/client_spec.rb +27 -455
- data/spec/config_helper.rb +4 -0
- data/spec/credentials_spec.rb +75 -0
- data/spec/curations_spec.rb +82 -0
- data/spec/documents_spec.rb +130 -0
- data/spec/engines_spec.rb +74 -0
- data/spec/list_documents_spec.rb +25 -0
- data/spec/logs_spec.rb +33 -0
- data/spec/meta_engines_spec.rb +73 -0
- data/spec/query_suggestion_spec.rb +37 -0
- data/spec/schema_spec.rb +23 -0
- data/spec/search_settings_spec.rb +65 -0
- data/spec/search_spec.rb +96 -0
- data/spec/spec_helper.rb +73 -0
- data/spec/synonyms_spec.rb +65 -0
- metadata +39 -4
@@ -7,11 +7,19 @@ module Elastic
|
|
7
7
|
module AppSearch
|
8
8
|
# API client for the {Elastic App Search API}[https://www.elastic.co/cloud/app-search-service].
|
9
9
|
class Client
|
10
|
+
autoload :Analytics, 'elastic/app-search/client/analytics'
|
11
|
+
autoload :Click, 'elastic/app-search/client/click'
|
12
|
+
autoload :Credentials, 'elastic/app-search/client/credentials'
|
13
|
+
autoload :Curations, 'elastic/app-search/client/curations'
|
10
14
|
autoload :Documents, 'elastic/app-search/client/documents'
|
11
15
|
autoload :Engines, 'elastic/app-search/client/engines'
|
16
|
+
autoload :MetaEngines, 'elastic/app-search/client/meta_engines'
|
17
|
+
autoload :Logs, 'elastic/app-search/client/logs'
|
18
|
+
autoload :Schema, 'elastic/app-search/client/schema'
|
12
19
|
autoload :Search, 'elastic/app-search/client/search'
|
13
|
-
autoload :QuerySuggestion, 'elastic/app-search/client/query_suggestion'
|
14
20
|
autoload :SearchSettings, 'elastic/app-search/client/search_settings'
|
21
|
+
autoload :Synonyms, 'elastic/app-search/client/synonyms'
|
22
|
+
autoload :QuerySuggestion, 'elastic/app-search/client/query_suggestion'
|
15
23
|
|
16
24
|
DEFAULT_TIMEOUT = 15
|
17
25
|
|
@@ -56,12 +64,20 @@ module Elastic
|
|
56
64
|
end
|
57
65
|
end
|
58
66
|
|
67
|
+
include Elastic::AppSearch::Client::Analytics
|
68
|
+
include Elastic::AppSearch::Client::Click
|
69
|
+
include Elastic::AppSearch::Client::Credentials
|
70
|
+
include Elastic::AppSearch::Client::Curations
|
59
71
|
include Elastic::AppSearch::Client::Documents
|
60
72
|
include Elastic::AppSearch::Client::Engines
|
73
|
+
include Elastic::AppSearch::Client::MetaEngines
|
74
|
+
include Elastic::AppSearch::Client::Logs
|
75
|
+
include Elastic::AppSearch::Client::Schema
|
61
76
|
include Elastic::AppSearch::Client::Search
|
77
|
+
include Elastic::AppSearch::Client::SearchSettings
|
62
78
|
include Elastic::AppSearch::Client::SignedSearchOptions
|
79
|
+
include Elastic::AppSearch::Client::Synonyms
|
63
80
|
include Elastic::AppSearch::Client::QuerySuggestion
|
64
|
-
include Elastic::AppSearch::Client::SearchSettings
|
65
81
|
end
|
66
82
|
end
|
67
83
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Analytics API - https://swiftype.com/documentation/app-search/api/analytics
|
2
|
+
module Elastic
|
3
|
+
module AppSearch
|
4
|
+
class Client
|
5
|
+
module Analytics
|
6
|
+
|
7
|
+
# Returns the number of clicks received by a document in descending order.
|
8
|
+
def get_top_clicks_analytics(engine_name, options)
|
9
|
+
post("engines/#{engine_name}/analytics/clicks", options)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns queries analytics by usage count
|
13
|
+
def get_top_queries_analytics(engine_name, options)
|
14
|
+
post("engines/#{engine_name}/analytics/queries", options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the number of clicks and total number of queries over a period.
|
18
|
+
def get_count_analytics(engine_name, options)
|
19
|
+
post("engines/#{engine_name}/analytics/counts", options)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Click API - https://swiftype.com/documentation/app-search/api/clickthrough
|
2
|
+
module Elastic
|
3
|
+
module AppSearch
|
4
|
+
class Client
|
5
|
+
module Click
|
6
|
+
|
7
|
+
# Send data about clicked results.
|
8
|
+
def log_click_through(engine_name, options)
|
9
|
+
post("engines/#{engine_name}/documents", options)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Credentials API - https://swiftype.com/documentation/app-search/api/credentials
|
2
|
+
module Elastic
|
3
|
+
module AppSearch
|
4
|
+
class Client
|
5
|
+
module Credentials
|
6
|
+
|
7
|
+
# Retrieve available credentials
|
8
|
+
def list_credentials(current: 1, size: 20)
|
9
|
+
get("credentials", :page => { :current => current, :size => size })
|
10
|
+
end
|
11
|
+
|
12
|
+
# Retrieve a credential
|
13
|
+
def get_credential(name)
|
14
|
+
get("credentials/#{name}")
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create a new credential
|
18
|
+
def create_credential(options)
|
19
|
+
post("credentials", options)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Update an existing credential
|
23
|
+
def update_credential(name, options)
|
24
|
+
put("credentials/#{name}", options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Destroy an existing credential
|
28
|
+
def destroy_credential(name)
|
29
|
+
delete("credentials/#{name}")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Curations API - https://swiftype.com/documentation/app-search/api/curations
|
2
|
+
module Elastic
|
3
|
+
module AppSearch
|
4
|
+
class Client
|
5
|
+
module Curations
|
6
|
+
|
7
|
+
# Retrieve available curations for the engine.
|
8
|
+
def list_curations(engine_name, current: 1, size: 20)
|
9
|
+
get("engines/#{engine_name}/curations", :page => { :current => current, :size => size })
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create a new curation.
|
13
|
+
def create_curation(engine_name, options)
|
14
|
+
post("engines/#{engine_name}/curations", options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Retrieve a curation by id.
|
18
|
+
def get_curation(engine_name, id)
|
19
|
+
get("engines/#{engine_name}/curations/#{id}")
|
20
|
+
end
|
21
|
+
|
22
|
+
# Update an existing curation.
|
23
|
+
def update_curation(engine_name, id, options)
|
24
|
+
put("engines/#{engine_name}/curations/#{id}", options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Delete a curation by id.
|
28
|
+
def destroy_curation(engine_name, id)
|
29
|
+
delete("engines/#{engine_name}/curations/#{id}")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -4,6 +4,7 @@ module Elastic
|
|
4
4
|
module AppSearch
|
5
5
|
class Client
|
6
6
|
module Engines
|
7
|
+
|
7
8
|
def list_engines(current: 1, size: 20)
|
8
9
|
get("engines", :page => { :current => current, :size => size })
|
9
10
|
end
|
@@ -21,6 +22,7 @@ module Elastic
|
|
21
22
|
def destroy_engine(engine_name)
|
22
23
|
delete("engines/#{engine_name}")
|
23
24
|
end
|
25
|
+
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Logs API - https://swiftype.com/documentation/app-search/api/logs
|
2
|
+
module Elastic
|
3
|
+
module AppSearch
|
4
|
+
class Client
|
5
|
+
module Logs
|
6
|
+
|
7
|
+
# The API Log displays API request and response data at the Engine level.
|
8
|
+
def get_api_logs(engine_name, options)
|
9
|
+
post("engines/#{engine_name}/logs/api", options)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Elastic
|
2
|
+
module AppSearch
|
3
|
+
class Client
|
4
|
+
module MetaEngines
|
5
|
+
|
6
|
+
ENGINE_TYPE_META = 'meta'.freeze()
|
7
|
+
|
8
|
+
def create_meta_engine(engine_name, source_engines)
|
9
|
+
post('engines', :name => engine_name, :type => ENGINE_TYPE_META, :source_engines => source_engines)
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_meta_engine_sources(engine_name, source_engines)
|
13
|
+
post("engines/#{engine_name}/source_engines", source_engines)
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete_meta_engine_sources(engine_name, source_engines)
|
17
|
+
delete("engines/#{engine_name}/source_engines", source_engines)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Schema API - https://swiftype.com/documentation/app-search/api/schema
|
2
|
+
module Elastic
|
3
|
+
module AppSearch
|
4
|
+
class Client
|
5
|
+
module Schema
|
6
|
+
|
7
|
+
# Retrieve schema for the current engine.
|
8
|
+
def get_schema(engine_name)
|
9
|
+
get("engines/#{engine_name}/schema")
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create a new schema field or update existing schema for the current engine.
|
13
|
+
def update_schema(engine_name, schema)
|
14
|
+
post("engines/#{engine_name}/schema", schema)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Synonyms API - https://swiftype.com/documentation/app-search/api/synonyms
|
2
|
+
module Elastic
|
3
|
+
module AppSearch
|
4
|
+
class Client
|
5
|
+
module Synonyms
|
6
|
+
|
7
|
+
# Retrieve available synonym sets for the engine.
|
8
|
+
def list_synonym_sets(engine_name, current: 1, size: 20)
|
9
|
+
get("engines/#{engine_name}/synonyms", :page => { :current => current, :size => size })
|
10
|
+
end
|
11
|
+
|
12
|
+
# Retrieve a synonym set by id
|
13
|
+
def get_synonym_set(engine_name, id)
|
14
|
+
get("engines/#{engine_name}/synonyms/#{id}")
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create a new synonym set
|
18
|
+
def create_synonym_set(engine_name, body)
|
19
|
+
post("engines/#{engine_name}/synonyms", body)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Update an existing synonym set
|
23
|
+
def update_synonym_set(engine_name, id, body)
|
24
|
+
put("engines/#{engine_name}/synonyms/#{id}", body)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Delete a synonym set by id
|
28
|
+
def destroy_synonym_set(engine_name, id)
|
29
|
+
delete("engines/#{engine_name}/synonyms/#{id}")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
describe Elastic::AppSearch::Client::Analytics do
|
2
|
+
include_context 'App Search Credentials'
|
3
|
+
include_context 'Test Engine'
|
4
|
+
|
5
|
+
let(:client) { Elastic::AppSearch::Client.new(client_options) }
|
6
|
+
|
7
|
+
context '#get_top_clicks_analytics' do
|
8
|
+
subject do
|
9
|
+
client.get_top_clicks_analytics(
|
10
|
+
engine_name,
|
11
|
+
:query => 'cats',
|
12
|
+
:page => {
|
13
|
+
:size => 20,
|
14
|
+
},
|
15
|
+
:filters => {
|
16
|
+
:date => {
|
17
|
+
:from => Time.now.iso8601,
|
18
|
+
:to => Time.now.iso8601
|
19
|
+
}
|
20
|
+
}
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'will query for analytics' do
|
25
|
+
expect(subject['results']).to(eq([]))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context '#get_top_queries_analytics' do
|
30
|
+
subject do
|
31
|
+
client.get_top_queries_analytics(
|
32
|
+
engine_name,
|
33
|
+
:page => {
|
34
|
+
:size => 20
|
35
|
+
},
|
36
|
+
:filters => {
|
37
|
+
:date => {
|
38
|
+
:from => Time.now.iso8601,
|
39
|
+
:to => Time.now.iso8601
|
40
|
+
}
|
41
|
+
}
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'will query for analytics' do
|
46
|
+
expect(subject['results']).to(eq([]))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context '#get_count_analytics' do
|
51
|
+
let(:from) { Time.now.iso8601 }
|
52
|
+
let(:to) { Time.now.iso8601 }
|
53
|
+
|
54
|
+
subject do
|
55
|
+
client.get_count_analytics(
|
56
|
+
engine_name,
|
57
|
+
:filters => {
|
58
|
+
:all => [
|
59
|
+
{
|
60
|
+
:tag => ['mobile', 'web']
|
61
|
+
}, {
|
62
|
+
:query => 'cats'
|
63
|
+
}, {
|
64
|
+
:document_id => '163'
|
65
|
+
}, {
|
66
|
+
:date => {
|
67
|
+
:from => from,
|
68
|
+
:to => to
|
69
|
+
}
|
70
|
+
}
|
71
|
+
]
|
72
|
+
},
|
73
|
+
:interval => 'hour'
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'will query for analytics' do
|
78
|
+
expect(subject['results'][0]['clicks']).to(eq(0))
|
79
|
+
expect(subject['results'][0]['queries']).to(eq(0))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/spec/click_spec.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
describe Elastic::AppSearch::Client::Click do
|
2
|
+
include_context 'App Search Credentials'
|
3
|
+
include_context 'Test Engine'
|
4
|
+
|
5
|
+
let(:client) { Elastic::AppSearch::Client.new(client_options) }
|
6
|
+
|
7
|
+
context '#log_click_through' do
|
8
|
+
let(:documents) { [first_document, second_document] }
|
9
|
+
let(:first_document_id) { 'id' }
|
10
|
+
let(:first_document) { { 'id' => first_document_id } }
|
11
|
+
let(:second_document_id) { 'another_id' }
|
12
|
+
let(:second_document) { { 'id' => second_document_id } }
|
13
|
+
|
14
|
+
before { client.index_documents(engine_name, documents) }
|
15
|
+
|
16
|
+
subject do
|
17
|
+
client.log_click_through(
|
18
|
+
engine_name,
|
19
|
+
:query => 'cat videos',
|
20
|
+
:document_id => first_document_id,
|
21
|
+
:request_id => 'e4c4dea0bd7ad3d2f676575ef16dc7d2',
|
22
|
+
:tags => ['firefox', 'web browser']
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'will log a click' do
|
27
|
+
expect(subject[0]['id']).not_to(be_empty)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/spec/client_spec.rb
CHANGED
@@ -2,42 +2,9 @@ require 'config_helper'
|
|
2
2
|
require 'securerandom'
|
3
3
|
|
4
4
|
describe Elastic::AppSearch::Client do
|
5
|
-
let(:engine_name) { "ruby-client-test-#{SecureRandom.hex}" }
|
6
|
-
|
7
5
|
include_context 'App Search Credentials'
|
8
6
|
let(:client) { Elastic::AppSearch::Client.new(client_options) }
|
9
7
|
|
10
|
-
before(:all) do
|
11
|
-
# Bootstraps a static engine for 'read-only' options that require indexing
|
12
|
-
# across the test suite
|
13
|
-
@static_engine_name = "ruby-client-test-static-#{SecureRandom.hex}"
|
14
|
-
as_api_key = ConfigHelper.get_as_api_key
|
15
|
-
as_host_identifier = ConfigHelper.get_as_host_identifier
|
16
|
-
as_api_endpoint = ConfigHelper.get_as_api_endpoint
|
17
|
-
client_options = ConfigHelper.get_client_options(as_api_key, as_host_identifier, as_api_endpoint)
|
18
|
-
@static_client = Elastic::AppSearch::Client.new(client_options)
|
19
|
-
@static_client.create_engine(@static_engine_name)
|
20
|
-
|
21
|
-
@document1 = { 'id' => '1', 'title' => 'The Great Gatsby' }
|
22
|
-
@document2 = { 'id' => '2', 'title' => 'Catcher in the Rye' }
|
23
|
-
@documents = [@document1, @document2]
|
24
|
-
@static_client.index_documents(@static_engine_name, @documents)
|
25
|
-
|
26
|
-
# Wait until documents are indexed
|
27
|
-
start = Time.now
|
28
|
-
ready = false
|
29
|
-
until (ready)
|
30
|
-
sleep(3)
|
31
|
-
results = @static_client.search(@static_engine_name, '')
|
32
|
-
ready = true if results['results'].length == 2
|
33
|
-
ready = true if (Time.now - start).to_i >= 120 # Time out after 2 minutes
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
after(:all) do
|
38
|
-
@static_client.destroy_engine(@static_engine_name)
|
39
|
-
end
|
40
|
-
|
41
8
|
describe '#create_signed_search_key' do
|
42
9
|
let(:key) { 'private-xxxxxxxxxxxxxxxxxxxx' }
|
43
10
|
let(:api_key_name) { 'private-key' }
|
@@ -52,435 +19,40 @@ describe Elastic::AppSearch::Client do
|
|
52
19
|
end
|
53
20
|
|
54
21
|
it 'should build a valid jwt' do
|
55
|
-
decoded_token = JWT.decode
|
56
|
-
expect(decoded_token[0]['api_key_name']).to
|
57
|
-
expect(decoded_token[0]['query']).to
|
22
|
+
decoded_token = JWT.decode(subject, key, true, :algorithm => 'HS256')
|
23
|
+
expect(decoded_token[0]['api_key_name']).to(eq(api_key_name))
|
24
|
+
expect(decoded_token[0]['query']).to(eq('cat'))
|
58
25
|
end
|
59
26
|
end
|
60
27
|
|
61
28
|
describe 'Requests' do
|
62
29
|
it 'should include client name and version in headers' do
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
.
|
67
|
-
:
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
context 'Documents' do
|
76
|
-
let(:document) { { 'url' => 'http://www.youtube.com/watch?v=v1uyQZNg2vE' } }
|
77
|
-
|
78
|
-
before do
|
79
|
-
client.create_engine(engine_name) rescue Elastic::AppSearch::BadRequest
|
80
|
-
end
|
81
|
-
|
82
|
-
after do
|
83
|
-
client.destroy_engine(engine_name) rescue Elastic::AppSearch::NonExistentRecord
|
84
|
-
end
|
85
|
-
|
86
|
-
describe '#index_document' do
|
87
|
-
subject { client.index_document(engine_name, document) }
|
88
|
-
|
89
|
-
it 'should return a processed document status hash' do
|
90
|
-
expect(subject).to match('id' => anything)
|
91
|
-
end
|
92
|
-
|
93
|
-
context 'when the document has an id' do
|
94
|
-
let(:id) { 'some_id' }
|
95
|
-
let(:document) { { 'id' => id, 'url' => 'http://www.youtube.com/watch?v=v1uyQZNg2vE' } }
|
96
|
-
|
97
|
-
it 'should return a processed document status hash with the same id' do
|
98
|
-
expect(subject).to eq('id' => id)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
context 'when a document has processing errors' do
|
103
|
-
let(:document) { { 'id' => 'too long' * 100 } }
|
104
|
-
|
105
|
-
it 'should raise an error when the API returns errors in the response' do
|
106
|
-
expect do
|
107
|
-
subject
|
108
|
-
end.to raise_error(Elastic::AppSearch::InvalidDocument, /Invalid field/)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
context 'when a document has a Ruby Time object' do
|
113
|
-
let(:time_rfc3339) { '2018-01-01T01:01:01+00:00' }
|
114
|
-
let(:time_object) { Time.parse(time_rfc3339) }
|
115
|
-
let(:document) { { 'created_at' => time_object } }
|
116
|
-
|
117
|
-
it 'should serialize the time object in RFC 3339' do
|
118
|
-
response = subject
|
119
|
-
expect(response).to have_key('id')
|
120
|
-
document_id = response.fetch('id')
|
121
|
-
expect do
|
122
|
-
documents = client.get_documents(engine_name, [document_id])
|
123
|
-
expect(documents.size).to eq(1)
|
124
|
-
expect(documents.first['created_at']).to eq(time_rfc3339)
|
125
|
-
end.to_not raise_error
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
describe '#index_documents' do
|
131
|
-
let(:documents) { [document, second_document] }
|
132
|
-
let(:second_document_id) { 'another_id' }
|
133
|
-
let(:second_document) { { 'id' => second_document_id, 'url' => 'https://www.youtube.com/watch?v=9T1vfsHYiKY' } }
|
134
|
-
subject { client.index_documents(engine_name, documents) }
|
135
|
-
|
136
|
-
it 'should return an array of document status hashes' do
|
137
|
-
expect(subject).to match(
|
138
|
-
[
|
139
|
-
{ 'id' => anything, 'errors' => [] },
|
140
|
-
{ 'id' => second_document_id, 'errors' => [] }
|
141
|
-
]
|
142
|
-
)
|
143
|
-
end
|
144
|
-
|
145
|
-
context 'when one of the documents has processing errors' do
|
146
|
-
let(:second_document) { { 'id' => 'too long' * 100 } }
|
147
|
-
|
148
|
-
it 'should return respective errors in an array of document processing hashes' do
|
149
|
-
expect(subject).to match(
|
150
|
-
[
|
151
|
-
{ 'id' => anything, 'errors' => [] },
|
152
|
-
{ 'id' => anything, 'errors' => ['Invalid field type: id must be less than 800 characters'] },
|
153
|
-
]
|
30
|
+
if (client_options[:api_endpoint])
|
31
|
+
stub_request(:any, "#{client_options[:api_endpoint]}engines")
|
32
|
+
client.list_engines
|
33
|
+
expect(WebMock).to(
|
34
|
+
have_requested(:get, "#{client_options[:api_endpoint]}engines")
|
35
|
+
.with(
|
36
|
+
:headers => {
|
37
|
+
'X-Swiftype-Client' => 'elastic-app-search-ruby',
|
38
|
+
'X-Swiftype-Client-Version' => Elastic::AppSearch::VERSION
|
39
|
+
}
|
154
40
|
)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
describe '#update_documents' do
|
160
|
-
let(:documents) { [document, second_document] }
|
161
|
-
let(:second_document_id) { 'another_id' }
|
162
|
-
let(:second_document) { { 'id' => second_document_id, 'url' => 'https://www.youtube.com/watch?v=9T1vfsHYiKY' } }
|
163
|
-
let(:updates) do
|
164
|
-
[
|
165
|
-
{
|
166
|
-
'id' => second_document_id,
|
167
|
-
'url' => 'https://www.example.com'
|
168
|
-
}
|
169
|
-
]
|
170
|
-
end
|
171
|
-
|
172
|
-
subject { client.update_documents(engine_name, updates) }
|
173
|
-
|
174
|
-
before do
|
175
|
-
client.index_documents(engine_name, documents)
|
176
|
-
end
|
177
|
-
|
178
|
-
# Note that since indexing a document takes up to a minute,
|
179
|
-
# we don't expect this to succeed, so we simply verify that
|
180
|
-
# the request responded with the correct 'id', even though
|
181
|
-
# the 'errors' object likely contains errors.
|
182
|
-
it 'should update existing documents' do
|
183
|
-
expect(subject).to match(['id' => second_document_id, 'errors' => anything])
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
describe '#get_documents' do
|
188
|
-
let(:documents) { [first_document, second_document] }
|
189
|
-
let(:first_document_id) { 'id' }
|
190
|
-
let(:first_document) { { 'id' => first_document_id, 'url' => 'https://www.youtube.com/watch?v=v1uyQZNg2vE' } }
|
191
|
-
let(:second_document_id) { 'another_id' }
|
192
|
-
let(:second_document) { { 'id' => second_document_id, 'url' => 'https://www.youtube.com/watch?v=9T1vfsHYiKY' } }
|
193
|
-
|
194
|
-
subject { client.get_documents(engine_name, [first_document_id, second_document_id]) }
|
195
|
-
|
196
|
-
before do
|
197
|
-
client.index_documents(engine_name, documents)
|
198
|
-
end
|
199
|
-
|
200
|
-
it 'will return documents by id' do
|
201
|
-
response = subject
|
202
|
-
expect(response.size).to eq(2)
|
203
|
-
expect(response[0]['id']).to eq(first_document_id)
|
204
|
-
expect(response[1]['id']).to eq(second_document_id)
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
describe '#list_documents' do
|
209
|
-
let(:documents) { [first_document, second_document] }
|
210
|
-
let(:first_document_id) { 'id' }
|
211
|
-
let(:first_document) { { 'id' => first_document_id, 'url' => 'https://www.youtube.com/watch?v=v1uyQZNg2vE' } }
|
212
|
-
let(:second_document_id) { 'another_id' }
|
213
|
-
let(:second_document) { { 'id' => second_document_id, 'url' => 'https://www.youtube.com/watch?v=9T1vfsHYiKY' } }
|
214
|
-
|
215
|
-
before do
|
216
|
-
client.index_documents(engine_name, documents)
|
217
|
-
end
|
218
|
-
|
219
|
-
context 'when no options are specified' do
|
220
|
-
it 'will return all documents' do
|
221
|
-
response = client.list_documents(engine_name)
|
222
|
-
expect(response['results'].size).to eq(2)
|
223
|
-
expect(response['results'][0]['id']).to eq(first_document_id)
|
224
|
-
expect(response['results'][1]['id']).to eq(second_document_id)
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
context 'when options are specified' do
|
229
|
-
it 'will return all documents' do
|
230
|
-
response = client.list_documents(engine_name, :page => { :size => 1, :current => 2 })
|
231
|
-
expect(response['results'].size).to eq(1)
|
232
|
-
expect(response['results'][0]['id']).to eq(second_document_id)
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
context 'Search' do
|
239
|
-
describe '#search' do
|
240
|
-
subject { @static_client.search(@static_engine_name, query, options) }
|
241
|
-
let(:query) { '' }
|
242
|
-
let(:options) { { 'page' => { 'size' => 2 } } }
|
243
|
-
|
244
|
-
it 'should execute a search query' do
|
245
|
-
expect(subject).to match(
|
246
|
-
'meta' => anything,
|
247
|
-
'results' => [anything, anything]
|
248
41
|
)
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
]
|
261
|
-
end
|
262
|
-
|
263
|
-
it 'should execute a multi search query' do
|
264
|
-
response = subject
|
265
|
-
expect(response).to match(
|
266
|
-
[
|
267
|
-
{
|
268
|
-
'meta' => anything,
|
269
|
-
'results' => [{ 'id' => { 'raw' => '1' }, 'title' => anything, '_meta' => anything }]
|
270
|
-
},
|
271
|
-
{
|
272
|
-
'meta' => anything,
|
273
|
-
'results' => [{ 'id' => { 'raw' => '2' }, 'title' => anything, '_meta' => anything }]
|
274
|
-
}
|
275
|
-
]
|
276
|
-
)
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
context 'when options are omitted' do
|
281
|
-
let(:queries) do
|
282
|
-
[
|
283
|
-
{ 'query' => 'gatsby' },
|
284
|
-
{ 'query' => 'catcher' }
|
285
|
-
]
|
286
|
-
end
|
287
|
-
|
288
|
-
it 'should execute a multi search query' do
|
289
|
-
response = subject
|
290
|
-
expect(response).to match(
|
291
|
-
[
|
292
|
-
{
|
293
|
-
'meta' => anything,
|
294
|
-
'results' => [{ 'id' => { 'raw' => '1' }, 'title' => anything, '_meta' => anything }]
|
295
|
-
},
|
296
|
-
{
|
297
|
-
'meta' => anything,
|
298
|
-
'results' => [{ 'id' => { 'raw' => '2' }, 'title' => anything, '_meta' => anything }]
|
299
|
-
}
|
300
|
-
]
|
301
|
-
)
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
context 'when a search is bad' do
|
306
|
-
let(:queries) do
|
307
|
-
[
|
308
|
-
{
|
309
|
-
'query' => 'cat',
|
310
|
-
'options' => { 'search_fields' => { 'taco' => {} } }
|
311
|
-
}, {
|
312
|
-
'query' => 'dog',
|
313
|
-
'options' => { 'search_fields' => { 'body' => {} } }
|
42
|
+
else
|
43
|
+
# CI runs against saas, so we keep this around for now. CI should be updated
|
44
|
+
# to use slef-managed and we should drop support "host_identifier" this.
|
45
|
+
stub_request(:any, "#{client_options[:host_identifier]}.api.swiftype.com/api/as/v1/engines")
|
46
|
+
client.list_engines
|
47
|
+
expect(WebMock).to(
|
48
|
+
have_requested(:get, "https://#{client_options[:host_identifier]}.api.swiftype.com/api/as/v1/engines")
|
49
|
+
.with(
|
50
|
+
:headers => {
|
51
|
+
'X-Swiftype-Client' => 'elastic-app-search-ruby',
|
52
|
+
'X-Swiftype-Client-Version' => Elastic::AppSearch::VERSION
|
314
53
|
}
|
315
|
-
]
|
316
|
-
end
|
317
|
-
|
318
|
-
it 'should throw an appropriate error' do
|
319
|
-
expect { subject }.to raise_error do |e|
|
320
|
-
expect(e).to be_a(Elastic::AppSearch::BadRequest)
|
321
|
-
expect(e.errors).to eq(['Search fields contains invalid field: taco', 'Search fields contains invalid field: body'])
|
322
|
-
end
|
323
|
-
end
|
324
|
-
end
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
context 'QuerySuggest' do
|
329
|
-
describe '#query_suggestion' do
|
330
|
-
let(:query) { 'cat' }
|
331
|
-
let(:options) { { :size => 3, :types => { :documents => { :fields => ['title'] } } } }
|
332
|
-
|
333
|
-
context 'when options are provided' do
|
334
|
-
subject { @static_client.query_suggestion(@static_engine_name, query, options) }
|
335
|
-
|
336
|
-
it 'should request query suggestions' do
|
337
|
-
expect(subject).to match(
|
338
|
-
'meta' => anything,
|
339
|
-
'results' => anything
|
340
|
-
)
|
341
|
-
end
|
342
|
-
end
|
343
|
-
|
344
|
-
context 'when options are omitted' do
|
345
|
-
subject { @static_client.query_suggestion(@static_engine_name, query) }
|
346
|
-
|
347
|
-
it 'should request query suggestions' do
|
348
|
-
expect(subject).to match(
|
349
|
-
'meta' => anything,
|
350
|
-
'results' => anything
|
351
54
|
)
|
352
|
-
|
353
|
-
end
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
context 'SearchSettings' do
|
358
|
-
let(:default_settings) { {
|
359
|
-
"search_fields" => {
|
360
|
-
"id" => {
|
361
|
-
"weight" => 1
|
362
|
-
}
|
363
|
-
},
|
364
|
-
"result_fields" => {"id"=>{"raw"=>{}}},
|
365
|
-
"boosts" => {}
|
366
|
-
} }
|
367
|
-
|
368
|
-
let(:updated_settings) { {
|
369
|
-
"search_fields" => {
|
370
|
-
"id" => {
|
371
|
-
"weight" => 3
|
372
|
-
}
|
373
|
-
},
|
374
|
-
"result_fields" => {"id"=>{"raw"=>{}}},
|
375
|
-
"boosts" => {}
|
376
|
-
} }
|
377
|
-
|
378
|
-
before(:each) do
|
379
|
-
client.create_engine(engine_name) rescue Elastic::AppSearch::BadRequest
|
380
|
-
end
|
381
|
-
|
382
|
-
after(:each) do
|
383
|
-
client.destroy_engine(engine_name) rescue Elastic::AppSearch::NonExistentRecord
|
384
|
-
end
|
385
|
-
|
386
|
-
describe '#show_settings' do
|
387
|
-
subject { client.show_settings(engine_name) }
|
388
|
-
|
389
|
-
it 'should return default settings' do
|
390
|
-
expect(subject).to match(default_settings)
|
391
|
-
end
|
392
|
-
end
|
393
|
-
|
394
|
-
describe '#update_settings' do
|
395
|
-
subject { client.show_settings(engine_name) }
|
396
|
-
|
397
|
-
before do
|
398
|
-
client.update_settings(engine_name, updated_settings)
|
399
|
-
end
|
400
|
-
|
401
|
-
it 'should update search settings' do
|
402
|
-
expect(subject).to match(updated_settings)
|
403
|
-
end
|
404
|
-
end
|
405
|
-
|
406
|
-
describe '#reset_settings' do
|
407
|
-
subject { client.show_settings(engine_name) }
|
408
|
-
|
409
|
-
before do
|
410
|
-
client.update_settings(engine_name, updated_settings)
|
411
|
-
client.reset_settings(engine_name)
|
412
|
-
end
|
413
|
-
|
414
|
-
it 'should reset search settings' do
|
415
|
-
expect(subject).to match(default_settings)
|
416
|
-
end
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
|
-
context 'Engines' do
|
421
|
-
after do
|
422
|
-
client.destroy_engine(engine_name) rescue Elastic::AppSearch::NonExistentRecord
|
423
|
-
end
|
424
|
-
|
425
|
-
context '#create_engine' do
|
426
|
-
it 'should create an engine when given a right set of parameters' do
|
427
|
-
expect { client.get_engine(engine_name) }.to raise_error(Elastic::AppSearch::NonExistentRecord)
|
428
|
-
client.create_engine(engine_name)
|
429
|
-
expect { client.get_engine(engine_name) }.to_not raise_error
|
430
|
-
end
|
431
|
-
|
432
|
-
it 'should accept an optional language parameter' do
|
433
|
-
expect { client.get_engine(engine_name) }.to raise_error(Elastic::AppSearch::NonExistentRecord)
|
434
|
-
client.create_engine(engine_name, 'da')
|
435
|
-
expect(client.get_engine(engine_name)).to match('name' => anything, 'type' => anything, 'language' => 'da')
|
436
|
-
end
|
437
|
-
|
438
|
-
it 'should return an engine object' do
|
439
|
-
engine = client.create_engine(engine_name)
|
440
|
-
expect(engine).to be_kind_of(Hash)
|
441
|
-
expect(engine['name']).to eq(engine_name)
|
442
|
-
end
|
443
|
-
|
444
|
-
it 'should return an error when the engine name has already been taken' do
|
445
|
-
client.create_engine(engine_name)
|
446
|
-
expect { client.create_engine(engine_name) }.to raise_error do |e|
|
447
|
-
expect(e).to be_a(Elastic::AppSearch::BadRequest)
|
448
|
-
expect(e.errors).to eq(['Name is already taken'])
|
449
|
-
end
|
450
|
-
end
|
451
|
-
end
|
452
|
-
|
453
|
-
context '#list_engines' do
|
454
|
-
it 'should return an array with a list of engines' do
|
455
|
-
expect(client.list_engines['results']).to be_an(Array)
|
456
|
-
end
|
457
|
-
|
458
|
-
it 'should include the engine name in listed objects' do
|
459
|
-
client.create_engine(engine_name)
|
460
|
-
|
461
|
-
engines = client.list_engines['results']
|
462
|
-
expect(engines.find { |e| e['name'] == engine_name }).to_not be_nil
|
463
|
-
end
|
464
|
-
|
465
|
-
it 'should include the engine name in listed objects with pagination' do
|
466
|
-
client.create_engine(engine_name)
|
467
|
-
|
468
|
-
engines = client.list_engines(:current => 1, :size => 20)['results']
|
469
|
-
expect(engines.find { |e| e['name'] == engine_name }).to_not be_nil
|
470
|
-
end
|
471
|
-
end
|
472
|
-
|
473
|
-
context '#destroy_engine' do
|
474
|
-
it 'should destroy the engine if it exists' do
|
475
|
-
client.create_engine(engine_name)
|
476
|
-
expect { client.get_engine(engine_name) }.to_not raise_error
|
477
|
-
|
478
|
-
client.destroy_engine(engine_name)
|
479
|
-
expect { client.get_engine(engine_name) }.to raise_error(Elastic::AppSearch::NonExistentRecord)
|
480
|
-
end
|
481
|
-
|
482
|
-
it 'should raise an error if the engine does not exist' do
|
483
|
-
expect { client.destroy_engine(engine_name) }.to raise_error(Elastic::AppSearch::NonExistentRecord)
|
55
|
+
)
|
484
56
|
end
|
485
57
|
end
|
486
58
|
end
|
@@ -489,12 +61,12 @@ describe Elastic::AppSearch::Client do
|
|
489
61
|
context 'host_identifier' do
|
490
62
|
it 'sets the base url correctly' do
|
491
63
|
client = Elastic::AppSearch::Client.new(:host_identifier => 'host-asdf', :api_key => 'foo')
|
492
|
-
expect(client.api_endpoint).to
|
64
|
+
expect(client.api_endpoint).to(eq('https://host-asdf.api.swiftype.com/api/as/v1/'))
|
493
65
|
end
|
494
66
|
|
495
67
|
it 'sets the base url correctly using deprecated as_host_key' do
|
496
68
|
client = Elastic::AppSearch::Client.new(:account_host_key => 'host-asdf', :api_key => 'foo')
|
497
|
-
expect(client.api_endpoint).to
|
69
|
+
expect(client.api_endpoint).to(eq('https://host-asdf.api.swiftype.com/api/as/v1/'))
|
498
70
|
end
|
499
71
|
end
|
500
72
|
end
|