From eb3eb91608c890d7e6a763f9572051de56a03936 Mon Sep 17 00:00:00 2001 From: ellnix Date: Fri, 24 Jan 2025 11:58:42 +0100 Subject: [PATCH 1/2] WIP: Support proxy search & its options --- .code-samples.meilisearch.yaml | 22 +++++++ lib/meilisearch.rb | 1 + lib/meilisearch/client.rb | 1 + lib/meilisearch/network.rb | 42 ++++++++++++ spec/meilisearch/client/proxy_spec.rb | 95 +++++++++++++++++++++++++++ 5 files changed, 161 insertions(+) create mode 100644 lib/meilisearch/network.rb create mode 100644 spec/meilisearch/client/proxy_spec.rb diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 99d2f4b0..0c15ebfe 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -712,3 +712,25 @@ distinct_attribute_guide_distinct_parameter_1: |- client.index('products').search('white shirt', { distinct: 'sku' }) +get_current_remote_uid_1: |- + client.self +set_current_remote_uid_1: |- + client.update_self(UID) +get_remotes_1: |- + client.remotes +add_remote_1: |- + client.add_remote(uid: REMOTE_UID, url: MEILISEARCH_URL, search_api_key: API_KEY) +get_remote_1: |- + client.remote(REMOTE_UID) +delete_remote_1: |- + client.delete_remote(REMOTE_UID) +remote_federated_search: |- + client.multi_search( + federation: { offset: 0, limit: 0 }, + queries: [ + { + # ... + federation_options: { remote: REMOTE_UID } + } + ] + ) 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 184d520d..dec4c9b4 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..5f9ada53 --- /dev/null +++ b/lib/meilisearch/network.rb @@ -0,0 +1,42 @@ +# https://meilisearch.notion.site/API-usage-Remote-search-request-f64fae093abf409e9434c9b9c8fab6f3 +module Meilisearch + module Network + # self has a certain meaning in ruby + # while it's not defined as a method in classes by default so there's nothing + # preventing us from having a method called "self", I think it could cause confusion + # + # perhaps "whoami" or "host" or "me" or "shard_name" could be alternatives? + # we could make an exception in the ruby sdk and call it remote_self but it sounds nonsensical + def self + http_get '/network/self' + end + + def update_self(new_uid) + # this is stated as both put and post in the notion doc + # will be determined when 1.13 guides are out I suppose + http_put '/network/self', new_uid.to_s + end + + def remotes + http_get '/network/remotes' + end + + def remote(uid) + http_get "/network/remotes/#{uid}" + end + + def add_remote(remote) + remote = Utils.trasform_attributes(remote) + http_post '/network/remotes', remote + end + + def update_remote(uid, remote_edits) + remote_edits = Utils.trasform_attributes(remote_edits) + http_patch "/network/remotes/#{uid}", remote_edits + end + + def delete_remote(uid) + http_delete "/network/remotes/#{uid}" + end + end +end diff --git a/spec/meilisearch/client/proxy_spec.rb b/spec/meilisearch/client/proxy_spec.rb new file mode 100644 index 00000000..8a68cf8f --- /dev/null +++ b/spec/meilisearch/client/proxy_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +describe 'Meilisearch::Client - proxy search' do + # Not sure yet what the default return from remotes is + # It will probably be { uid: 'default', url: client_url, ...} + let(:default_remotes) { nil } + + let(:sample_remote) do + { + uid: 'ms-2', + url: 'localhost:7701', + search_api_key: 'masterKey' + } + end + + # Experimental feature must be enabled before these specs + # but the PR for that is yet to be merged + describe '#self' do + it 'returns the name of the current shard' do + # from what I can see default shard name remains to be documented + # following the naming of embedders, I guessed 'default' + expect(client.self).to eq 'default' + end + end + + describe '#update_self' do + it 'switches to a different shard' do + client.add_remote( + uid: 'ms-2', + url: 'ms-gaunt-hovel.meilisearch.com:7700', + search_api_key: 'masterKey' + ) + + # I'm unaware if this is supposed to return a task yet + # I imagine it's just updating a string, but there could + # be things the engine needs to do upon this value being set + client.update_self('ms-2') + expect(client.self).to eq 'ms-2' + ensure + client.delete_remote('ms-2') + end + end + + describe '#remotes' do + it 'lists all remotes' do + expect(client.remotes).to eq(default_remotes) + end + end + + describe '#remote' do + it 'returns a specific remote' do + client.add_remote(sample_remote) + + expect(client.remote('ms-2')).to include(*sample_remote.values) + ensure + client.delete_remote('ms-2') + end + end + + describe '#add_remote' do + it 'adds new shards' do + client.add_remote(sample_remote) + + expect(client.remotes).to include(sample_remote) + ensure + client.delete_remote('ms-2') + end + end + + describe '#update_remote' do + it 'edits an existing shard entry' do + client.add_remote(sample_remote) + + edit = { search_api_key: 'plaintextSecret' } + client.update_remote('ms-2', edit) + + expected = sample_remote.merge(edit) + actual = client.remote('ms-2') + + expect(actual).to eq(expected) + ensure + client.delete_remote('ms-2') + end + end + + describe '#delete_remote' do + it 'deletes a shard from uid' do + client.add_remote(sample_remote) + expect(client.remotes).to include(sample_remote) + + client.delete_remote('ms-2') + expect(client.remotes).to eq(default_remotes) + end + end +end From 00ea4ec36caabc4143bf91e32ecdc1209281bfb0 Mon Sep 17 00:00:00 2001 From: ellnix Date: Fri, 24 Jan 2025 13:47:08 +0100 Subject: [PATCH 2/2] WIP: Add support for testing proxies --- CONTRIBUTING.md | 11 +++++++- spec/meilisearch/client/proxy_spec.rb | 38 ++++++++++++++++++++++++-- spec/spec_helper.rb | 4 +++ spec/support/default_shared_context.rb | 1 + 4 files changed, 51 insertions(+), 3 deletions(-) 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/spec/meilisearch/client/proxy_spec.rb b/spec/meilisearch/client/proxy_spec.rb index 8a68cf8f..8964c94c 100644 --- a/spec/meilisearch/client/proxy_spec.rb +++ b/spec/meilisearch/client/proxy_spec.rb @@ -8,8 +8,8 @@ let(:sample_remote) do { uid: 'ms-2', - url: 'localhost:7701', - search_api_key: 'masterKey' + url: PROXY_URL, + search_api_key: MASTER_KEY } end @@ -92,4 +92,38 @@ expect(client.remotes).to eq(default_remotes) end end + + describe 'searching multiple remotes' do + # this of course means we need to support federated search, + # which has not been merged yet. + it 'federated search searches multiple remotes' do + 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]) + + sherwood_forest = { id: 50, name: 'Sherwood Forest' } + forbidden_forest = { id: 200, name: 'Forbidden Forest' } + client.index('parks').add_documents([sherwood_forest, forbidden_forest]) + + client.add_remote(sample_remote) + + response = client.multi_search( + federation: {}, + queries: [ + { + q: 'Forest', + index_uid: 'parks' + }, + { + q: 'Body', + index_uid: 'books', + remote: 'ms-2' + } + ] + ) + + expect(response['hits']).to include(three_body_problem, sherwood_forest, forbidden_forest) + expect(response['hits']).not_to include(the_dark_forest) + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 18170866..2230e83f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -42,6 +42,10 @@ # 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_URL_PROXY', 'localhost'), port: ENV.fetch('MEILISEARCH_PORT_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 10ccd72e..e3cec9fa 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)