diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 596ad49d..e0c5441f 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -721,3 +721,39 @@ multi_search_federated_1: |- queries: [{ index_uid: 'movies', q: 'batman' }, { index_uid: 'comics', q: 'batman' }], federation: {} ) +multi_search_remote_federated_1: |- + client.multi_search( + federation: {}, + queries: [ + { + index_uid: 'movies', + q: 'batman', + federation_options: { + remote: 'ms-00' + } + }, + { + index_uid: 'movies', + q: 'batman', + federation_options: { + remote: 'ms-01' + } + } + ] + ) +get_network_1: |- + client.network +update_network_1: |- + client.update_network( + self: 'ms-00', + remotes: { + 'ms-00': { + 'url': 'http://INSTANCE_URL', + 'searchApiKey': 'INSTANCE_API_KEY' + }, + 'ms-01': { + 'url': 'http://ANOTHER_INSTANCE_URL', + 'searchApiKey': 'ANOTHER_INSTANCE_API_KEY' + } + } + ) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f1c6692..f625de39 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,10 +34,14 @@ First of all, thank you for contributing to Meilisearch! The goal of this docume You can set up your local environment natively or using `docker`, check out the [`docker-compose.yml`](/docker-compose.yml). +#### With Docker Example of running all the checks with docker: ```bash docker-compose run --rm package bash -c "bundle install && bundle exec rspec && bundle exec rubocop" ``` + + +#### Locally To install dependencies: ```bash @@ -51,7 +55,12 @@ Each PR should pass the tests to be accepted. ```bash # Tests curl -L https://install.meilisearch.com | sh # download Meilisearch -./meilisearch --master-key=masterKey --no-analytics # run Meilisearch + +# run Meilisearch +./meilisearch --master-key=masterKey --no-analytics +# run a second instance of Meilisearch to act as proxy +./meilisearch --master-key=masterKey --no-analytics --http-addr localhost:7701 + bundle exec rspec ``` diff --git a/docker-compose.yml b/docker-compose.yml index 1c94bad7..7b9be814 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,19 +10,29 @@ services: environment: - MEILISEARCH_URL=meilisearch - MEILISEARCH_PORT=7700 + - MEILISEARCH_REMOTE_URL=meilisearch_remote + - MEILISEARCH_REMOTE_PORT=7701 - BUNDLE_PATH=/vendor/bundle depends_on: - meilisearch - links: - - meilisearch + - meilisearch_remote volumes: - ./:/home/package - bundle:/vendor/bundle meilisearch: - image: getmeili/meilisearch:latest + image: getmeili/meilisearch:nightly ports: - "7700" environment: - MEILI_MASTER_KEY=masterKey - MEILI_NO_ANALYTICS=true + + meilisearch_remote: + image: getmeili/meilisearch:nightly + ports: + - "7701" + environment: + - MEILI_MASTER_KEY=masterKey + - MEILI_NO_ANALYTICS=true + command: ["meilisearch", "--http-addr", "0.0.0.0:7701"] diff --git a/lib/meilisearch.rb b/lib/meilisearch.rb index fc7e15e2..7dc558a1 100644 --- a/lib/meilisearch.rb +++ b/lib/meilisearch.rb @@ -7,6 +7,7 @@ require 'meilisearch/models/task' require 'meilisearch/http_request' require 'meilisearch/multi_search' +require 'meilisearch/network' require 'meilisearch/tenant_token' require 'meilisearch/task' require 'meilisearch/client' diff --git a/lib/meilisearch/client.rb b/lib/meilisearch/client.rb index cbce86ac..f92a6b40 100644 --- a/lib/meilisearch/client.rb +++ b/lib/meilisearch/client.rb @@ -4,6 +4,7 @@ module Meilisearch class Client < HTTPRequest include Meilisearch::TenantToken include Meilisearch::MultiSearch + include Meilisearch::Network ### INDEXES diff --git a/lib/meilisearch/network.rb b/lib/meilisearch/network.rb new file mode 100644 index 00000000..4d767c4b --- /dev/null +++ b/lib/meilisearch/network.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Meilisearch + module Network + def network + http_get '/network' + end + + def update_network(new_network) + new_network = Utils.transform_attributes(new_network) + http_patch '/network', new_network + end + end +end diff --git a/lib/meilisearch/utils.rb b/lib/meilisearch/utils.rb index ca7bbaa0..a62df7c4 100644 --- a/lib/meilisearch/utils.rb +++ b/lib/meilisearch/utils.rb @@ -83,6 +83,7 @@ def warn_on_non_conforming_attribute_names(body) def parse(body) body + .transform_values { |val| transform_attributes(val) } .transform_keys(&:to_s) .transform_keys do |key| key.include?('_') ? key.downcase.gsub(SNAKE_CASE, &:upcase).gsub('_', '') : key diff --git a/spec/meilisearch/client/multi_search_spec.rb b/spec/meilisearch/client/multi_search_spec.rb index ab7b7b33..63da512d 100644 --- a/spec/meilisearch/client/multi_search_spec.rb +++ b/spec/meilisearch/client/multi_search_spec.rb @@ -75,4 +75,61 @@ expect(hits.size).to be 3 expect(hits.first).to have_key('_federation') end + + describe 'searching multiple remotes' do + it 'federated search searches multiple remotes' do + client.update_experimental_features(network: true) + proxy_client.update_experimental_features(network: true) + + client.update_network( + self: 'ms0', + remotes: { + ms2: { + url: PROXY_URL, + search_api_key: MASTER_KEY + } + } + ) + + three_body_problem = { id: 1, title: 'The Three Body Problem' } + the_dark_forest = { id: 2, title: 'The Dark Forest' } + proxy_client.index('books').add_documents([three_body_problem, the_dark_forest]).await + + sherwood_forest = { id: 50, name: 'Sherwood Forest' } + forbidden_forest = { id: 200, name: 'Forbidden Forest' } + client.index('parks').add_documents([sherwood_forest, forbidden_forest]).await + + response = client.multi_search( + federation: {}, + queries: [ + { + q: 'Forest', + index_uid: 'parks', + federation_options: { + remote: 'ms0' + } + }, + { + q: 'Body', + index_uid: 'books', + federation_options: { + remote: 'ms2' + } + } + ] + ) + + resp = response['hits'].map { |hit| hit.slice('id', 'name', 'title').transform_keys(&:to_sym) } + + expect(resp).to include(three_body_problem, sherwood_forest, forbidden_forest) + expect(resp).not_to include(the_dark_forest) + rescue Meilisearch::CommunicationError + pending('Please launch a second instance of Meilisearch to test network search, see spec_helper for addr config.') + raise + ensure + client.update_network(self: nil, remotes: nil) + client.delete_index('parks') + proxy_client.delete_index('books') + end + end end diff --git a/spec/meilisearch/client/network_spec.rb b/spec/meilisearch/client/network_spec.rb new file mode 100644 index 00000000..7e02412a --- /dev/null +++ b/spec/meilisearch/client/network_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +describe 'Meilisearch::Client - Network' do + before do + client.update_experimental_features(network: true) + end + + let(:default_network) do + { + 'self' => nil, + 'remotes' => {} + } + end + + let(:sample_remote) do + { + ms1: { + url: 'http://localhost', + search_api_key: 'masterKey' + } + } + end + + describe '#network' do + it 'returns the sharding configuration' do + expect(client.network).to eq default_network + end + end + + describe '#update_network' do + it 'updates the sharding configuration' do + new_network = { + self: 'ms0', + remotes: sample_remote + } + + client.update_network(new_network) + expect(client.network).to eq(Meilisearch::Utils.transform_attributes(new_network)) + + client.update_network({ remotes: nil, self: nil }) + expect(client.network).to eq default_network + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3144dee8..25e3cf96 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -42,6 +42,11 @@ # Globals for all tests URL = format('http://%s:%s', host: ENV.fetch('MEILISEARCH_URL', 'localhost'), port: ENV.fetch('MEILISEARCH_PORT', '7700')) + +PROXY_URL = format('http://%s:%s', + host: ENV.fetch('MEILISEARCH_REMOTE_URL', 'localhost'), + port: ENV.fetch('MEILISEARCH_REMOTE_PROXY', '7701')) + MASTER_KEY = 'masterKey' DEFAULT_SEARCH_RESPONSE_KEYS = [ 'hits', diff --git a/spec/support/default_shared_context.rb b/spec/support/default_shared_context.rb index 552f4e54..2e36963e 100644 --- a/spec/support/default_shared_context.rb +++ b/spec/support/default_shared_context.rb @@ -2,6 +2,7 @@ RSpec.shared_context 'test defaults' do let(:client) { Meilisearch::Client.new(URL, MASTER_KEY, { timeout: 2, max_retries: 1 }) } + let(:proxy_client) { Meilisearch::Client.new(PROXY_URL, MASTER_KEY, { timeout: 2, max_retries: 1 }) } before do clear_all_indexes(client)