Multi-cluster management is deprecated and will be sunset.
If you have issues with your Algolia infrastructure
due to large volumes of data, contact the Algolia support
team.
When your data no longer fits on a single machine, you have to add new clusters. Multi-Cluster Management (MCM) simplifies this process by letting you distribute and manage your data across several machines.
For example, a music streaming application lets users create public and private playlists.
With MCM, the number of playlists can grow without fearing they might exceed your current cluster’s size limit.
You also get dedicated user access out of the box.
Splitting data across multiple clusters
Assigning user data to a cluster
Without MCM, an application is bound to a single cluster. It means that splitting data across several clusters requires you to assign data to a specific application ID, so you can manually maintain and orchestrate the distribution by yourself.
With MCM, all clusters use the same application ID. MCM keeps a mapping on each cluster to route requests to the correct cluster. All you need to do is assign a cluster to a user with the assignUserId
method.
1
2
3
4
| var response = await client.AssignUserIdAsync(
"user42",
new AssignUserIdParams { Cluster = "d4242-eu" }
);
|
1
2
3
4
5
6
| final response = await client.assignUserId(
xAlgoliaUserID: "user42",
assignUserIdParams: AssignUserIdParams(
cluster: "d4242-eu",
),
);
|
1
2
3
4
5
6
7
| response, err := client.AssignUserId(client.NewApiAssignUserIdRequest(
"user42",
search.NewEmptyAssignUserIdParams().SetCluster("d4242-eu")))
if err != nil {
// handle the eventual error
panic(err)
}
|
1
| client.assignUserId("user42", new AssignUserIdParams().setCluster("d4242-eu"));
|
1
| const response = await client.assignUserId({ xAlgoliaUserID: 'user42', assignUserIdParams: { cluster: 'd4242-eu' } });
|
1
2
3
4
5
6
| var response = client.assignUserId(
xAlgoliaUserID = "user42",
assignUserIdParams = AssignUserIdParams(
cluster = "d4242-eu",
),
)
|
1
2
3
4
5
| $response = $client->assignUserId(
'user42',
['cluster' => 'd4242-eu',
],
);
|
1
2
3
4
5
6
| response = client.assign_user_id(
x_algolia_user_id="user42",
assign_user_id_params={
"cluster": "d4242-eu",
},
)
|
1
| response = client.assign_user_id("user42", Algolia::Search::AssignUserIdParams.new(cluster: "d4242-eu"))
|
1
2
3
4
5
6
7
8
9
| val response = Await.result(
client.assignUserId(
xAlgoliaUserID = "user42",
assignUserIdParams = AssignUserIdParams(
cluster = "d4242-eu"
)
),
Duration(100, "sec")
)
|
1
2
3
4
| let response = try await client.assignUserId(
xAlgoliaUserID: "user42",
assignUserIdParams: AssignUserIdParams(cluster: "d4242-eu")
)
|
Adding private user data
Imagine that you want to index your users’ private playlists.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| const playlists = [
{
user: 'user42',
name: 'My peaceful playlist',
tracks: [
// ...
],
createdAt: 1500000181
},
{
user: 'user4242',
name: 'My workout playlist',
tracks: [
// ...
],
createdAt: 1500040452
}
];
|
Without MCM, every record needs a userID
attribute to tag the right user. Additionally, you need to set the userID
attribute as a filter at query time.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| namespace Algolia;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using Algolia.Search.Clients;
using Algolia.Search.Http;
using Algolia.Search.Models.Search;
class SetSettingsThenSaveObjects
{
private static readonly List<Dictionary<string, object>> playlists = []; // Your records
private static string GetAppIdFor(string user)
{
return ""; // Implement your own logic here
}
private static string GetIndexingApiKeyFor(string user)
{
return ""; // Implement your own logic here
}
async Task Main(string[] args)
{
foreach (var playlist in playlists)
{
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
var appId = GetAppIdFor((playlist.GetValueOrDefault("user", "") as string)!);
var apiKey = GetIndexingApiKeyFor((playlist.GetValueOrDefault("user", "") as string)!);
try
{
var client = new SearchClient(new SearchConfig(appId, apiKey));
var settings = new IndexSettings { AttributesForFaceting = ["searchable(playlistName)"] };
await client.SetSettingsAsync("<YOUR_INDEX_NAME>", settings);
await client.SaveObjectsAsync("<YOUR_INDEX_NAME>", playlists);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| import 'package:algolia_client_search/algolia_client_search.dart';
final playlists = []; // Your records
String getAppIDFor(String _) {
return ""; // Implement your own logic here
}
String getIndexingApiKeyFor(String _) {
return ""; // Implement your own logic here
}
void setSettingsThenSaveObjects() async {
for (final playlist in playlists) {
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
final appId = getAppIDFor(playlist["user"]);
final apiKey = getIndexingApiKeyFor(playlist["user"]);
final client = SearchClient(appId: appId, apiKey: apiKey);
final settings = IndexSettings(
attributesForFaceting: ['filterOnly(userID)'],
);
await client.setSettings(
indexName: "<YOUR_INDEX_NAME>",
indexSettings: settings,
);
final batchParams = BatchWriteParams(
requests: playlists
.map((record) => BatchRequest(
action: Action.addObject,
body: record,
))
.toList());
await client.batch(
indexName: "<YOUR_INDEX_NAME>",
batchWriteParams: batchParams,
);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
| package main
import (
"fmt"
"github.com/algolia/algoliasearch-client-go/v4/algolia/search"
)
func getAppIDFor(_ string) (string, error) {
return "", nil // Implement your logic here
}
func getIndexingApiKeyFor(_ string) (string, error) {
return "", nil // Implement your logic here
}
func setSettingsThenSaveObjects() {
playlists := []map[string]any{{ /* Your records */ }}
for _, playlist := range playlists {
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
appID, err := getAppIDFor(playlist["user"].(string))
if err != nil {
fmt.Println(err)
return
}
apiKey, err := getIndexingApiKeyFor(playlist["user"].(string))
if err != nil {
fmt.Println(err)
return
}
client, err := search.NewClient(appID, apiKey)
if err != nil {
fmt.Println(err)
return
}
settings := &search.IndexSettings{
AttributesForFaceting: []string{"filterOnly(user)"},
}
_, err = client.SetSettings(client.NewApiSetSettingsRequest(
"<YOUR_INDEX_NAME>", settings))
if err != nil {
panic(err)
}
_, err = client.SaveObjects(
"<YOUR_INDEX_NAME>", playlists)
if err != nil {
panic(err)
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| package com.algolia;
import com.algolia.api.SearchClient;
import com.algolia.config.*;
import com.algolia.model.search.*;
import java.util.List;
import java.util.Map;
public class setSettingsThenSaveObjects {
private static final List<Map<String, Object>> playlists = List.of(/* Your records */);
private static String getAppIDFor(String user) {
return ""; // Implement your own logic here
}
private static String getIndexingApiKeyFor(String user) {
return ""; // Implement your own logic here
}
public static void main(String[] args) throws Exception {
playlists.forEach(playlist -> {
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
String appID = getAppIDFor((String) playlist.get("user"));
String apiKey = getIndexingApiKeyFor((String) playlist.get("user"));
try (SearchClient client = new SearchClient(appID, apiKey)) {
IndexSettings settings = new IndexSettings().setAttributesForFaceting(List.of("searchable(playlistName)"));
client.setSettings("<YOUR_INDEX_NAME>", settings);
client.saveObjects("<YOUR_INDEX_NAME>", playlists);
} catch (Exception e) {
System.out.println("An error occurred: " + e.getMessage());
}
});
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| import type { IndexSettings } from 'algoliasearch';
import { algoliasearch } from 'algoliasearch';
const playlists: Record<string, any>[] = [
/* Your records */
];
const getAppIDFor = (_: string) => {
return ''; // Implement your own logic here
};
const getIndexingApiKeyFor = (_: string) => {
return ''; // Implement your own logic here
};
playlists.forEach(async (playlist) => {
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
const appID = getAppIDFor(playlist['user']);
const apiKey = getIndexingApiKeyFor(playlist['user']);
try {
const client = algoliasearch(appID, apiKey);
const settings: IndexSettings = {
attributesForFaceting: ['filterOnly(user)'],
};
await client.setSettings({ indexName: 'indexName', indexSettings: settings });
await client.saveObjects({ indexName: 'indexName', objects: playlists });
} catch (e: any) {
console.error(e);
}
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| import kotlinx.serialization.json.JsonObject
import com.algolia.client.api.SearchClient
import com.algolia.client.configuration.*
import com.algolia.client.transport.*
import com.algolia.client.extensions.*
import com.algolia.client.model.search.*
val playlists = listOf<JsonObject>() // Your records
val getAppIDFor: (String) -> String = {
"" // Implement your own logic here
}
val getIndexingApiKeyFor: (String) -> String = {
"" // Implement your own logic here
}
suspend fun setSettingsThenSaveObjects() {
playlists.forEach { playlist ->
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
val appID = getAppIDFor(playlist["user"].toString())
val apiKey = getIndexingApiKeyFor(playlist["user"].toString())
val client = SearchClient(appID, apiKey)
val settings = IndexSettings(
attributesForFaceting = listOf("filterOnly(user)"),
)
try {
client.setSettings(
indexName = "<YOUR_INDEX_NAME>",
indexSettings = settings,
)
client.saveObjects(
indexName = "<YOUR_INDEX_NAME>",
objects = playlists,
)
} catch (exception: Exception) {
println(exception.message)
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| <?php
require __DIR__.'/../vendor/autoload.php';
use Algolia\AlgoliaSearch\Api\SearchClient;
use Algolia\AlgoliaSearch\Model\Search\IndexSettings;
$playlists = [/* Your records */];
$getAppIDFor = function ($user) {
return ''; // Implement your own logic here
};
$getIndexingApiKeyFor = function ($user) {
return ''; // Implement your own logic here
};
foreach ($playlists as $playlist) {
try {
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
$appID = $getAppIDFor($playlist['user']);
$apiKey = $getIndexingApiKeyFor($playlist['user']);
$client = SearchClient::create($appID, $apiKey);
$settings = [
(new IndexSettings())
->setAttributesForFaceting(['filterOnly(user)']),
];
$client->setSettings(
'<YOUR_INDEX_NAME>',
$settings,
);
$client->saveObjects(
'<YOUR_INDEX_NAME>',
$playlists,
);
} catch (Exception $e) {
echo $e->getMessage().PHP_EOL;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| from algoliasearch.search.client import SearchClientSync
from algoliasearch.search.models.index_settings import IndexSettings
def _get_app_id_for(_user):
# Implement your own logic here
return ""
def _get_indexing_api_key_for(_user):
# Implement your own logic here
return ""
playlists = [] # Your records
for playlist in playlists:
app_id = _get_app_id_for(playlist["user"])
api_key = _get_indexing_api_key_for(playlist["user"])
_client = SearchClientSync(app_id, api_key)
settings = IndexSettings(attributes_for_faceting=["filterOnly(user)"])
try:
_client.set_settings(
index_name="<YOUR_INDEX_NAME>",
index_settings=settings,
)
_client.save_objects(
index_name="<YOUR_INDEX_NAME>",
objects=playlists,
)
except Exception as e:
print(f"Error: {e}")
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| require "algolia"
def get_app_id_for(_user)
# Implement your own logic here
""
end
def get_indexing_api_key_for(_user)
# Implement your own logic here
""
end
# Your records
playlists = []
playlists.each do |playlist|
begin
app_id = get_app_id_for(playlist["user"])
api_key = get_indexing_api_key_for(playlist["user"])
client = Algolia::SearchClient.create(app_id, api_key)
settings = Algolia::Search::IndexSettings.new(
attributes_for_faceting: ["filterOnly(user)"]
)
client.set_settings("<YOUR_INDEX_NAME>", settings)
client.save_objects("<YOUR_INDEX_NAME>", playlists)
rescue Exception => e
puts("An error occurred: #{e}")
end
end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import algoliasearch.api.SearchClient
import algoliasearch.config.*
import algoliasearch.extension.SearchClientExtensions
import algoliasearch.search.IndexSettings
val getAppIDFor: String => String = _ => {
"" // Implement your own logic here
}
val getIndexingApiKeyFor: String => String = _ => {
"" // Implement your own logic here
}
def setSettingsThenSaveObjects(): Future[Unit] = {
val playlists: Seq[Map[String, Any]] = Seq() // Your records
playlists.foreach { playlist =>
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
val appID = getAppIDFor(playlist("user").toString)
val apiKey = getIndexingApiKeyFor(playlist("user").toString)
val client = SearchClient(appID, apiKey)
val settings = IndexSettings(
attributesForFaceting = Some(Seq("filterOnly(user)"))
)
Await.result(
client.setSettings(
indexName = "<YOUR_INDEX_NAME>",
indexSettings = settings
),
Duration(5, "sec")
)
Await.result(
client.saveObjects(
indexName = "<YOUR_INDEX_NAME>",
objects = playlists
),
Duration(5, "sec")
)
}
Future.unit
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| import Foundation
#if os(Linux) // For linux interop
import FoundationNetworking
#endif
import Core
import Search
let playlists: [[String: AnyCodable]] = [ /* Your records */ ]
let getAppIDFor = { (_: String) in "" } // Implement your own logic here
let getIndexingApiKeyFor = { (_: String) in "" } // Implement your own logic here
func setSettingsThenSaveObjects() async throws {
for playlist in playlists {
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
let appID = getAppIDFor(playlist["user"]?.value as! String)
let apiKey = getIndexingApiKeyFor(playlist["user"]?.value as! String)
do {
let client = try SearchClient(appID: appID, apiKey: apiKey)
let settings = IndexSettings(
attributesForFaceting: ["filterOnly(user)"]
)
try await client.setSettings(indexName: "<YOUR_INDEX_NAME>", indexSettings: settings)
try await client.saveObjects(indexName: "<YOUR_INDEX_NAME>", objects: playlists)
} catch {
print(error)
}
}
}
|
With MCM, all records are automatically tagged using the X-Algolia-User-ID
header you send at query time. The engine automatically adds an extra attribute __userID__
inside records to identify the userID
associated with each record.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| namespace Algolia;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using Algolia.Search.Clients;
using Algolia.Search.Http;
using Algolia.Search.Models.Search;
class SetHeaderUserIDThenSaveObjects
{
private static readonly List<Dictionary<string, object>> playlists = []; // Your records
async Task Main(string[] args)
{
var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY"));
foreach (var playlist in playlists)
{
try
{
var playlistUserID = playlist.GetValueOrDefault("userID", "") as string;
await client.SaveObjectsAsync(
"<YOUR_INDEX_NAME>",
playlists,
false,
1000,
new RequestOptionBuilder().AddExtraHeader("X-Algolia-User-ID", playlistUserID).Build()
);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| import 'package:algolia_client_search/algolia_client_search.dart';
final playlists = []; // Your records
void setHeaderUserIDThenSaveObjects() async {
final client =
SearchClient(appId: 'ALGOLIA_APPLICATION_ID', apiKey: 'ALGOLIA_API_KEY');
for (final playlist in playlists) {
final playlistUserID = playlist["userID"];
final batchParams = BatchWriteParams(
requests: playlists
.map((record) => BatchRequest(
action: Action.addObject,
body: record,
))
.toList());
await client.batch(
indexName: "<YOUR_INDEX_NAME>",
batchWriteParams: batchParams,
requestOptions: RequestOptions(
headers: {
'X-Algolia-User-ID': playlistUserID,
},
));
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| package main
import "github.com/algolia/algoliasearch-client-go/v4/algolia/search"
func setHeaderUserIDThenSaveObjects() {
playlists := []map[string]any{{ /* Your records */ }}
client, err := search.NewClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")
if err != nil {
// The client can fail to initialize if you pass an invalid parameter.
panic(err)
}
for _, playlist := range playlists {
playlistUserID := playlist["userID"]
_, err := client.SaveObjects(
"<YOUR_INDEX_NAME>", playlists, search.WithWaitForTasks(false), search.WithBatchSize(1000), search.WithHeaderParam("X-Algolia-User-ID", playlistUserID))
if err != nil {
panic(err)
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| package com.algolia;
import com.algolia.api.SearchClient;
import com.algolia.config.*;
import com.algolia.model.search.*;
import java.util.List;
import java.util.Map;
public class setHeaderUserIDThenSaveObjects {
private static final List<Map<String, Object>> playlists = List.of(/* Your records */);
public static void main(String[] args) throws Exception {
try (SearchClient client = new SearchClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY");) {
playlists.forEach(playlist -> {
String playlistUserID = (String) playlist.get("userID");
client.saveObjects(
"<YOUR_INDEX_NAME>",
playlists,
false,
1000,
new RequestOptions().addExtraHeader("X-Algolia-User-ID", playlistUserID)
);
});
} catch (Exception e) {
System.out.println("An error occurred: " + e.getMessage());
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| import { algoliasearch } from 'algoliasearch';
const playlists: Record<string, any>[] = [
/* Your records */
];
const client = algoliasearch('ALGOLIA_APPLICATION_ID', 'ALGOLIA_API_KEY');
playlists.forEach(async (playlist) => {
try {
const playlistUserID = playlist['userID'];
await client.saveObjects(
{ indexName: 'indexName', objects: playlists, waitForTasks: false, batchSize: 1000 },
{
headers: { 'X-Algolia-User-ID': playlistUserID },
},
);
} catch (e: any) {
console.error(e);
}
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| import kotlinx.serialization.json.JsonObject
import com.algolia.client.api.SearchClient
import com.algolia.client.configuration.*
import com.algolia.client.transport.*
import com.algolia.client.extensions.*
import com.algolia.client.model.search.*
suspend fun setHeaderUserIDThenSaveObjects() {
val playlists: List<JsonObject> = listOf() // Your records
val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")
playlists.forEach { playlist ->
val playlistUserID = playlist["userID"].toString()
try {
client.saveObjects(
indexName = "<YOUR_INDEX_NAME>",
objects = playlists,
waitForTasks = false,
batchSize = 1000,
requestOptions = RequestOptions(
headers = buildMap {
put("X-Algolia-User-ID", playlistUserID)
},
),
)
} catch (exception: Exception) {
println(exception.message)
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| <?php
require __DIR__.'/../vendor/autoload.php';
use Algolia\AlgoliaSearch\Api\SearchClient;
$playlists = [/* Your records */];
$client = SearchClient::create('ALGOLIA_APPLICATION_ID', 'ALGOLIA_API_KEY');
foreach ($playlists as $playlist) {
try {
$playlistUserID = $playlist['userID'];
$client->saveObjects(
'<YOUR_INDEX_NAME>',
$playlists,
false,
1000,
[
'headers' => [
'X-Algolia-User-ID' => $playlistUserID,
],
]
);
} catch (Exception $e) {
echo $e->getMessage().PHP_EOL;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| from algoliasearch.search.client import SearchClientSync
from json import loads
playlists = [] # Your records
_client = SearchClientSync("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")
for playlist in playlists:
try:
playlist_user_id = playlist["userID"]
_client.save_objects(
index_name="<YOUR_INDEX_NAME>",
objects=playlists,
wait_for_tasks=False,
batch_size=1000,
request_options={
"headers": loads("""{"X-Algolia-User-ID":playlistUserID}"""),
},
)
except Exception as e:
print(f"Error: {e}")
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| require "algolia"
# Your records
playlists = []
client = Algolia::SearchClient.create("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")
playlists.each do |playlist|
playlist_user_id = playlist["userID"]
client.save_objects(
"<YOUR_INDEX_NAME>",
playlists,
false,
1000,
{:header_params => {"X-Algolia-User-ID" => playlist_user_id}}
)
end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import algoliasearch.api.SearchClient
import algoliasearch.config.*
import algoliasearch.extension.SearchClientExtensions
def setHeaderUserIDThenSaveObjects(): Future[Unit] = {
val playlists: Seq[Map[String, Any]] = Seq() // Your records
val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")
playlists.foreach { playlist =>
val playlistUserID = playlist("userID").toString
Await.result(
client.saveObjects(
indexName = "<YOUR_INDEX_NAME>",
objects = playlists,
waitForTasks = false,
batchSize = 1000,
requestOptions = Some(
RequestOptions
.builder()
.withHeader("X-Algolia-User-ID", "playlistUserID")
.build()
)
),
Duration(5, "sec")
)
}
Future.unit
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| import Foundation
#if os(Linux) // For linux interop
import FoundationNetworking
#endif
import Core
import Search
func setHeaderUserIDThenSaveObjects() async throws {
let playlists: [[String: AnyCodable]] = [ /* Your records */ ]
let client = try SearchClient(appID: "ALGOLIA_APPLICATION_ID", apiKey: "ALGOLIA_API_KEY")
for playlist in playlists {
do {
let playlistUserID = playlist["userID"]?.value as! String
try await client.saveObjects(
indexName: "<YOUR_INDEX_NAME>",
objects: playlists,
waitForTasks: false,
batchSize: 1000,
requestOptions: RequestOptions(
headers: ["X-Algolia-User-ID": playlistUserID]
)
)
} catch {
print(error)
}
}
}
|
For simplicity’s sake, the preceding snippets add records one by one.
For better performance, you should group your records by userID
first, then batch them.
Adding public data
If you decide you want to index public playlists as well.
1
2
3
4
5
6
7
8
9
10
| const playlists = [
{
user: 'public',
name: 'Hot 100 Billboard Charts',
tracks: [
// ...
],
createdAt: 1500240452,
},
];
|
Without MCM, you need to tag every public record with a particular value (such as “public”) for the userID
attribute. Then, you need to filter on that value to search for public records.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| namespace Algolia;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using Algolia.Search.Clients;
using Algolia.Search.Http;
using Algolia.Search.Models.Search;
class SaveObjectsMcm
{
private readonly List<Dictionary<string, object>> playlists = []; // Your records
private static List<Dictionary<string, string>> GetAllAppIdConfigurations()
{
return
[ /* A list of your MCM AppID/ApiKey pairs */
];
}
async Task Main(string[] args)
{
// Fetch from your own data storage and with your own code
// the list of application IDs and API keys to target each cluster
var configurations = GetAllAppIdConfigurations();
// Send the records to each cluster
foreach (var config in configurations)
{
try
{
var client = new SearchClient(
new SearchConfig(
config.GetValueOrDefault("appID", ""),
config.GetValueOrDefault("apiKey", "")
)
);
await client.SaveObjectsAsync("<YOUR_INDEX_NAME>", playlists);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| import 'package:algolia_client_search/algolia_client_search.dart';
List<Map<String, String>> getAllAppIDConfigurations() {
return [/* A list of your MCM AppID/ApiKey pairs */];
}
List<Map> playlists = [/* Your records */];
void saveObjectsMCM() async {
final configurations = getAllAppIDConfigurations();
for (var configuration in configurations) {
final appId = configuration['appID'] ?? "";
final apiKey = configuration['apiKey'] ?? "";
final client = SearchClient(appId: appId, apiKey: apiKey);
try {
final batchParams = BatchWriteParams(
requests: playlists
.map((record) => BatchRequest(
action: Action.addObject,
body: record,
))
.toList());
await client.batch(
indexName: "<YOUR_INDEX_NAME>",
batchWriteParams: batchParams,
);
} catch (e) {
throw Exception('Error for appID $appId: $e');
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| package main
import (
"fmt"
"github.com/algolia/algoliasearch-client-go/v4/algolia/search"
)
func getAllAppIDConfigurations() ([]struct{ appID, apiKey string }, error) {
return []struct{ appID, apiKey string }{ /* A list of your MCM AppID/ApiKey pairs */ }, nil
}
func saveObjectsMCM() {
playlists := []map[string]any{{ /* Your records */ }}
// Fetch from your own data storage and with your own code
// the list of application IDs and API keys to target each cluster
configurations, err := getAllAppIDConfigurations()
if err != nil {
fmt.Println(err)
return
}
// Send the records to each cluster
for _, configuration := range configurations {
client, err := search.NewClient(configuration.appID, configuration.apiKey)
if err != nil {
fmt.Println(err)
return
}
_, err = client.SaveObjects(
"<YOUR_INDEX_NAME>", playlists)
if err != nil {
panic(err)
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| package com.algolia;
import com.algolia.api.SearchClient;
import com.algolia.config.*;
import com.algolia.model.search.*;
import java.util.List;
import java.util.Map;
public class saveObjectsMCM {
private static final List<Map<String, Object>> playlists = List.of(/* Your records */);
private static List<Map<String, String>> getAllAppIDConfigurations() {
return List.of(/* A list of your MCM AppID/ApiKey pairs */);
}
public static void main(String[] args) throws Exception {
// Fetch from your own data storage and with your own code
// the list of application IDs and API keys to target each cluster
var configurations = getAllAppIDConfigurations();
// Send the records to each cluster
configurations.forEach(config -> {
try (SearchClient client = new SearchClient(config.get("appID"), config.get("apiKey"))) {
client.saveObjects("<YOUR_INDEX_NAME>", playlists);
} catch (Exception e) {
System.out.println("An error occurred: " + e.getMessage());
}
});
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| import { algoliasearch } from 'algoliasearch';
const getAllAppIDConfigurations = (): Record<string, string>[] => {
return [
/* A list of your MCM AppID/ApiKey pairs */
];
};
const playlists: Record<string, any>[] = [
/* Your records */
];
// Fetch from your own data storage and with your own code
// the list of application IDs and API keys to target each cluster
const configurations = getAllAppIDConfigurations();
// Send the records to each cluster
Object.keys(configurations).forEach(async (appID) => {
try {
const client = algoliasearch(appID, configurations[appID]);
await client.saveObjects({ indexName: 'indexName', objects: playlists });
} catch (e: any) {
console.error(e);
}
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| import kotlinx.serialization.json.JsonObject
import com.algolia.client.api.SearchClient
import com.algolia.client.configuration.*
import com.algolia.client.transport.*
import com.algolia.client.extensions.*
import com.algolia.client.model.search.*
val getAllAppIDConfigurations: () -> Map<String, String> = {
mapOf() // A map of your MCM AppID/ApiKey pairs
}
suspend fun saveObjectsMCM() {
val playlists: List<JsonObject> = listOf() // Your records
val configurations = getAllAppIDConfigurations()
configurations.map { (appID, apiKey) ->
val client = SearchClient(appID, apiKey)
try {
client.saveObjects(
indexName = "<YOUR_INDEX_NAME>",
objects = playlists,
)
} catch (e: Exception) {
throw Exception("Error for appID $appID: ${e.message}")
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| <?php
require __DIR__.'/../vendor/autoload.php';
use Algolia\AlgoliaSearch\Api\SearchClient;
$getAllAppIDConfigurations = function () {
return [/* A list of your MCM AppID/ApiKey pairs */];
};
$playlists = [/* Your records */];
// Fetch from your own data storage and with your own code
// the list of application IDs and API keys to target each cluster
$configurations = $getAllAppIDConfigurations();
// Send the records to each cluster
foreach ($configurations as [$appID, $apiKey]) {
try {
$client = SearchClient::create($appID, $apiKey);
$client->saveObjects(
'<YOUR_INDEX_NAME>',
$playlists,
);
} catch (Exception $e) {
echo $e->getMessage().PHP_EOL;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| from algoliasearch.search.client import SearchClientSync
def _get_all_app_id_configurations():
return [] # A list of your MCM AppID/ApiKey pairs
playlists = [] # Your records
# Fetch from your own data storage and with your own code
# the list of application IDs and API keys to target each cluster
configurations = _get_all_app_id_configurations()
# Send the records to each cluster
for appID, apiKey in configurations:
try:
_client = SearchClientSync(appID, apiKey)
_client.save_objects(
index_name="<YOUR_INDEX_NAME>",
objects=playlists,
)
except Exception as e:
print(f"Error: {e}")
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| require "algolia"
def get_all_app_id_configurations
# A list of your MCM AppID/ApiKey pairs
[]
end
# Your records
playlists = []
# Fetch from your own data storage and with your own code
# the list of application IDs and API keys to target each cluster
configurations = get_all_app_id_configurations
# Send the records to each cluster
configurations.each do |appID, apiKey|
begin
client = Algolia::SearchClient.create(appID, apiKey)
client.save_objects("<YOUR_INDEX_NAME>", playlists)
rescue Exception => e
puts("An error occurred: #{e}")
end
end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import algoliasearch.api.SearchClient
import algoliasearch.config.*
import algoliasearch.extension.SearchClientExtensions
import algoliasearch.config.RequestOptions
val getAllAppIDConfigurations: () => Map[String, String] = () => {
Map( /* A map of your MCM AppID/ApiKey pairs */ )
}
val playlists: Seq[Any] = Seq( /* Your records */ )
def saveObjectsMCM(): Future[Unit] = {
val configurations = getAllAppIDConfigurations()
Future
.sequence {
configurations.map { case (appID, apiKey) =>
val client = new SearchClient(appID, apiKey)
client
.saveObjects(
indexName = "<YOUR_INDEX_NAME>",
objects = playlists
)
.recover { case ex: Exception =>
println(s"Error for appID $appID: ${ex.getMessage}")
}
}
}
.map(_ => ())
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| import Foundation
#if os(Linux) // For linux interop
import FoundationNetworking
#endif
import Core
import Search
let getAllAppIDConfigurations: () -> [(String, String)] = {
[ /* A list of your MCM AppID/ApiKey pairs */ ]
}
func saveObjectsMCM() async throws {
let playlists: [[String: AnyCodable]] = [ /* Your records */ ]
// Fetch from your own data storage and with your own code
// the list of application IDs and API keys to target each cluster
let configurations = getAllAppIDConfigurations()
// Send the records to each cluster
for (appID, apiKey) in configurations {
do {
let client = try SearchClient(appID: appID, apiKey: apiKey)
try await client.saveObjects(indexName: "<YOUR_INDEX_NAME>", objects: playlists)
} catch {
print(error)
}
}
}
|
With MCM, you can use the userID
value *
to flag records as public, allowing all users to see them. Public records are automatically replicated on all clusters to avoid network latency during search.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| namespace Algolia;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using Algolia.Search.Clients;
using Algolia.Search.Http;
using Algolia.Search.Models.Search;
class SaveObjectsPublicUser
{
private static readonly List<Dictionary<string, object>> playlists = []; // Your records
async Task Main(string[] args)
{
var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY"));
await client.SaveObjectsAsync(
"<YOUR_INDEX_NAME>",
playlists,
false,
1000,
new RequestOptionBuilder().AddExtraHeader("X-Algolia-User-ID", "*").Build()
);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| import 'package:algolia_client_search/algolia_client_search.dart';
final playlists = [/* Your records */];
void saveObjectsPublicUser() async {
final client =
SearchClient(appId: 'ALGOLIA_APPLICATION_ID', apiKey: 'ALGOLIA_API_KEY');
final batchParams = BatchWriteParams(
requests: playlists
.map((record) => BatchRequest(
action: Action.addObject,
body: record,
))
.toList());
await client.batch(
indexName: "<YOUR_INDEX_NAME>",
batchWriteParams: batchParams,
requestOptions: RequestOptions(
headers: {
'X-Algolia-User-ID': "*",
},
));
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| package main
import "github.com/algolia/algoliasearch-client-go/v4/algolia/search"
func saveObjectsPublicUser() {
playlists := []map[string]any{{ /* Your records */ }}
client, err := search.NewClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")
if err != nil {
// The client can fail to initialize if you pass an invalid parameter.
panic(err)
}
_, err = client.SaveObjects(
"<YOUR_INDEX_NAME>", playlists, search.WithWaitForTasks(false), search.WithBatchSize(1000), search.WithHeaderParam("X-Algolia-User-ID", "*"))
if err != nil {
panic(err)
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| package com.algolia;
import com.algolia.api.SearchClient;
import com.algolia.config.*;
import com.algolia.model.search.*;
import java.util.List;
import java.util.Map;
public class saveObjectsPublicUser {
private static final List<Map<String, Object>> playlists = List.of(/* Your records */);
public static void main(String[] args) throws Exception {
try (SearchClient client = new SearchClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY");) {
client.saveObjects("<YOUR_INDEX_NAME>", playlists, false, 1000, new RequestOptions().addExtraHeader("X-Algolia-User-ID", "*"));
} catch (Exception e) {
System.out.println("An error occurred: " + e.getMessage());
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
| import { algoliasearch } from 'algoliasearch';
const playlists: Record<string, any>[] = []; // Your records
const client = algoliasearch('ALGOLIA_APPLICATION_ID', 'ALGOLIA_API_KEY');
await client.saveObjects(
{ indexName: 'indexName', objects: playlists, waitForTasks: false, batchSize: 1000 },
{
headers: { 'X-Algolia-User-ID': '*' },
},
);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| import kotlinx.serialization.json.JsonObject
import com.algolia.client.api.SearchClient
import com.algolia.client.configuration.*
import com.algolia.client.transport.*
import com.algolia.client.extensions.*
import com.algolia.client.model.search.*
import com.algolia.client.transport.RequestOptions
suspend fun saveObjectsPublicUser() {
val playlists: List<JsonObject> = listOf() // Your records
val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")
client.saveObjects(
indexName = "<YOUR_INDEX_NAME>",
objects = playlists,
waitForTasks = false,
batchSize = 1000,
requestOptions = RequestOptions(
headers = buildMap {
put("X-Algolia-User-ID", "*")
},
),
)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| <?php
require __DIR__.'/../vendor/autoload.php';
use Algolia\AlgoliaSearch\Api\SearchClient;
$playlists = [/* Your records */];
$client = SearchClient::create('ALGOLIA_APPLICATION_ID', 'ALGOLIA_API_KEY');
$client->saveObjects(
'<YOUR_INDEX_NAME>',
$playlists,
false,
1000,
[
'headers' => [
'X-Algolia-User-ID' => '*',
],
]
);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| from algoliasearch.search.client import SearchClientSync
from json import loads
playlists = [] # Your records
_client = SearchClientSync("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")
_client.save_objects(
index_name="<YOUR_INDEX_NAME>",
objects=playlists,
wait_for_tasks=False,
batch_size=1000,
request_options={
"headers": loads("""{"X-Algolia-User-ID":"*"}"""),
},
)
|
1
2
3
4
5
6
7
8
| require "algolia"
# Your records
playlists = []
client = Algolia::SearchClient.create("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")
client.save_objects("<YOUR_INDEX_NAME>", playlists, false, 1000, {:header_params => {"X-Algolia-User-ID" => "*"}})
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import algoliasearch.api.SearchClient
import algoliasearch.config.*
import algoliasearch.extension.SearchClientExtensions
import algoliasearch.config.RequestOptions
def saveObjectsPublicUser(): Future[Unit] = {
val playlists: Seq[Any] = Seq() // Your records
val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")
Await.result(
client
.saveObjects(
indexName = "<YOUR_INDEX_NAME>",
objects = playlists,
waitForTasks = false,
batchSize = 1000,
requestOptions = Some(
RequestOptions
.builder()
.withHeader("X-Algolia-User-ID", "*")
.build()
)
)
.map(_ => Future.unit),
Duration(5, "sec")
)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| import Foundation
#if os(Linux) // For linux interop
import FoundationNetworking
#endif
import Core
import Search
func saveObjectsPublicUser() async throws {
let playlists: [[String: AnyCodable]] = [] // Your records
let client = try SearchClient(appID: "ALGOLIA_APPLICATION_ID", apiKey: "ALGOLIA_API_KEY")
try await client.saveObjects(
indexName: "<YOUR_INDEX_NAME>",
objects: playlists,
waitForTasks: false,
batchSize: 1000,
requestOptions: RequestOptions(
headers: ["X-Algolia-User-ID": "*"]
)
)
}
|
Searching the data
Without MCM, you need to manually handle the filtering logic to filter on private data for a specific user and public data.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| namespace Algolia;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using Algolia.Search.Clients;
using Algolia.Search.Http;
using Algolia.Search.Models.Search;
class McmSearchWithout
{
private static string GetAppIdFor(string user)
{
return ""; // Implement your own logic here
}
private static string GetIndexingApiKeyFor(string user)
{
return ""; // Implement your own logic here
}
async Task Main(string[] args)
{
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
var appId = GetAppIdFor("user42");
var apiKey = GetIndexingApiKeyFor("user42");
var client = new SearchClient(new SearchConfig(appId, apiKey));
var searchParams = new SearchParams(
new SearchParamsObject
{
Query = "<YOUR_SEARCH_QUERY>",
FacetFilters = new FacetFilters(
[new FacetFilters("user:user42"), new FacetFilters("user:public")]
),
}
);
await client.SearchSingleIndexAsync<Hit>("<YOUR_INDEX_NAME>", searchParams);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| import 'package:algolia_client_search/algolia_client_search.dart';
String getAppIDFor(String _) {
return ""; // Implement your own logic here
}
String getIndexingApiKeyFor(String _) {
return ""; // Implement your own logic here
}
void mcmSearchWithout() async {
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
final appId = getAppIDFor("user42");
final apiKey = getIndexingApiKeyFor("user42");
final client = SearchClient(appId: appId, apiKey: apiKey);
final searchParams = SearchParamsObject(
query: "<YOUR_SEARCH_QUERY>",
facetFilters: ["user:user42", "user:public"]);
await client.searchSingleIndex(
indexName: "<YOUR_INDEX_NAME>",
searchParams: searchParams,
);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| package main
import (
"fmt"
"github.com/algolia/algoliasearch-client-go/v4/algolia/search"
)
func mcmSearchWithout() {
getAppIDFor := func(_ string) (string, error) {
return "", nil // Implement your logic here
}
getIndexingApiKeyFor := func(_ string) (string, error) {
return "", nil // Implement your logic here
}
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
appID, err := getAppIDFor("user42")
if err != nil {
fmt.Println(err)
return
}
apiKey, err := getIndexingApiKeyFor("user42")
if err != nil {
fmt.Println(err)
return
}
client, err := search.NewClient(appID, apiKey)
if err != nil {
fmt.Println(err)
return
}
searchParams := search.SearchParamsObjectAsSearchParams(
search.NewSearchParamsObject().
SetQuery("<YOUR_SEARCH_QUERY>").
SetFacetFilters(
search.ArrayOfFacetFiltersAsFacetFilters(
[]search.FacetFilters{
*search.StringAsFacetFilters("user:user42"),
*search.StringAsFacetFilters("user:public"),
},
),
),
)
_, err = client.SearchSingleIndex(client.NewApiSearchSingleIndexRequest(
"<YOUR_INDEX_NAME>").WithSearchParams(searchParams))
if err != nil {
panic(err)
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| package com.algolia;
import com.algolia.api.SearchClient;
import com.algolia.config.*;
import com.algolia.model.search.*;
import java.util.List;
public class mcmSearchWithout {
private static String getAppIDFor(String user) {
return ""; // Implement your own logic here
}
private static String getIndexingApiKeyFor(String user) {
return ""; // Implement your own logic here
}
public static void main(String[] args) throws Exception {
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
String appID = getAppIDFor("user42");
String apiKey = getIndexingApiKeyFor("user42");
try (SearchClient client = new SearchClient(appID, apiKey)) {
SearchParams searchParams = new SearchParamsObject()
.setQuery("<YOUR_SEARCH_QUERY>")
.setFacetFilters(FacetFilters.of(List.of(FacetFilters.of("user:user42"), FacetFilters.of("user:public"))));
client.searchSingleIndex("<YOUR_INDEX_NAME>", searchParams, Hit.class);
} catch (Exception e) {
System.out.println("An error occurred: " + e.getMessage());
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| import type { SearchParams } from 'algoliasearch';
import { algoliasearch } from 'algoliasearch';
const getAppIDFor = (_: string) => {
return ''; // Implement your own logic here
};
const getIndexingApiKeyFor = (_: string) => {
return ''; // Implement your own logic here
};
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
const appID = getAppIDFor('user42');
const apiKey = getIndexingApiKeyFor('user42');
const client = algoliasearch(appID, apiKey);
const searchParams: SearchParams = {
query: '<YOUR_SEARCH_QUERY>',
facetFilters: ['user:user42', 'user:public'],
};
await client.searchSingleIndex({ indexName: 'indexName', searchParams: searchParams });
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| import com.algolia.client.api.SearchClient
import com.algolia.client.configuration.*
import com.algolia.client.transport.*
import com.algolia.client.extensions.*
import com.algolia.client.model.search.*
suspend fun mcmSearchWithout() {
val getAppIDFor: (String) -> String = {
"" // Implement your own logic here
}
val getIndexingApiKeyFor: (String) -> String = {
"" // Implement your own logic here
}
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
val appID = getAppIDFor("user42")
val apiKey = getIndexingApiKeyFor("user42")
val client = SearchClient(appID, apiKey)
val searchParams = SearchParamsObject(
query = "<YOUR_SEARCH_QUERY>",
facetFilters = FacetFilters.of(
listOf(
FacetFilters.of("user:user42"),
FacetFilters.of("user:public"),
),
),
)
client.searchSingleIndex(
indexName = "<YOUR_INDEX_NAME>",
searchParams = searchParams,
)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| <?php
require __DIR__.'/../vendor/autoload.php';
use Algolia\AlgoliaSearch\Api\SearchClient;
use Algolia\AlgoliaSearch\Model\Search\SearchParamsObject;
$getAppIDFor = function ($user) {
return ''; // Implement your own logic here
};
$getIndexingApiKeyFor = function ($user) {
return ''; // Implement your own logic here
};
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
$appID = $getAppIDFor('user42');
$apiKey = $getIndexingApiKeyFor('user42');
$client = SearchClient::create($appID, $apiKey);
$searchParams = (new SearchParamsObject())
->setQuery('<YOUR_SEARCH_QUERY>')
->setFacetFilters(['user:user42', 'user:public'])
;
$client->searchSingleIndex(
'<YOUR_INDEX_NAME>',
$searchParams,
);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| from algoliasearch.search.client import SearchClientSync
def _get_app_id_for(_user):
# Implement your own logic here
return ""
def _get_indexing_api_key_for(_user):
# Implement your own logic here
return ""
app_id = _get_app_id_for("user42")
api_key = _get_indexing_api_key_for("user42")
_client = SearchClientSync(app_id, api_key)
search_params = {
"query": "<YOUR_SEARCH_QUERY>",
"facetFilters": ["user:user42", "user:public"],
}
_client.search_single_index(
index_name="<YOUR_INDEX_NAME>",
search_params=search_params,
)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| import(time)
require "algolia"
def get_app_id_for(_user)
# Implement your own logic here
""
end
def get_indexing_api_key_for(_user)
# Implement your own logic here
""
end
app_id = get_app_id_for("user42")
api_key = get_indexing_api_key_for("user42")
client = Algolia::SearchClient.create(app_id, api_key)
search_params = Algolia::Search::SearchParamsObject.new(
query: "<YOUR_SEARCH_QUERY>",
facet_filters: %w[user:user42 user:public]
)
client.search_single_index("<YOUR_INDEX_NAME>", search_params)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import algoliasearch.api.SearchClient
import algoliasearch.config.*
import algoliasearch.extension.SearchClientExtensions
import algoliasearch.search.{FacetFilters, SearchParamsObject}
def mcmSearchWithout(): Future[Unit] = {
val getAppIDFor: String => String = _ => {
"" // Implement your own logic here
}
val getIndexingApiKeyFor: String => String = _ => {
"" // Implement your own logic here
}
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
val appID = getAppIDFor("user42")
val apiKey = getIndexingApiKeyFor("user42")
val client = SearchClient(appID, apiKey)
val searchParams = SearchParamsObject(
query = Some("<YOUR_SEARCH_QUERY>"),
facetFilters = Some(
FacetFilters.SeqOfFacetFilters(
Seq(
FacetFilters.StringValue("user:user42"),
FacetFilters.StringValue("user:public")
)
)
)
)
client
.searchSingleIndex(
indexName = "<YOUR_INDEX_NAME>",
searchParams = Some(searchParams)
)
.map { response =>
println(response)
}
.recover { case ex: Exception =>
println(s"An error occurred: ${ex.getMessage}")
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| import Foundation
#if os(Linux) // For linux interop
import FoundationNetworking
#endif
import Core
import Search
func mcmSearchWithout() async throws {
let getAppIDFor = { (_: String) in "" } // Implement your own logic here
let getIndexingApiKeyFor = { (_: String) in "" } // Implement your own logic here
// Fetch from your own data storage and with your own code
// the associated application ID and API key for this user
let appID = getAppIDFor("user42")
let apiKey = getIndexingApiKeyFor("user42")
let client = try SearchClient(appID: appID, apiKey: apiKey)
let searchParams = SearchSearchParams.searchSearchParamsObject(
SearchSearchParamsObject(
query: "<YOUR_SEARCH_QUERY>",
facetFilters: .arrayOfSearchFacetFilters([.string("user:user42"), .string("user:public")])
)
)
let response: SearchResponse<Hit> = try await client.searchSingleIndex(
indexName: "<YOUR_INDEX_NAME>",
searchParams: searchParams
)
print(response)
}
|
With MCM, all the engine needs is the userID
(with the X-Algolia-User-ID
header) that the query targets, so it can route the request to the right cluster. It automatically adds the right filters to retrieve both the user’s data and public data.
1
2
3
4
5
| var response = await client.SearchSingleIndexAsync<Hit>(
"ALGOLIA_INDEX_NAME",
new SearchParams(new SearchParamsObject { Query = "peace" }),
new RequestOptionBuilder().AddExtraHeader("X-Algolia-User-ID", "user42").Build()
);
|
1
2
3
4
5
6
7
8
9
10
11
| final response = await client.searchSingleIndex(
indexName: "ALGOLIA_INDEX_NAME",
searchParams: SearchParamsObject(
query: "peace",
),
requestOptions: RequestOptions(
headers: {
'X-Algolia-User-ID': 'user42',
},
),
);
|
1
2
3
4
5
6
7
| response, err := client.SearchSingleIndex(client.NewApiSearchSingleIndexRequest(
"ALGOLIA_INDEX_NAME").WithSearchParams(search.SearchParamsObjectAsSearchParams(
search.NewEmptySearchParamsObject().SetQuery("peace"))), search.WithHeaderParam("X-Algolia-User-ID", "user42"))
if err != nil {
// handle the eventual error
panic(err)
}
|
1
2
3
4
5
6
| client.searchSingleIndex(
"ALGOLIA_INDEX_NAME",
new SearchParamsObject().setQuery("peace"),
Hit.class,
new RequestOptions().addExtraHeader("X-Algolia-User-ID", "user42")
);
|
1
2
3
4
5
6
| const response = await client.searchSingleIndex(
{ indexName: 'playlists', searchParams: { query: 'peace' } },
{
headers: { 'X-Algolia-User-ID': 'user42' },
},
);
|
1
2
3
4
5
6
7
8
9
10
11
| var response = client.searchSingleIndex(
indexName = "ALGOLIA_INDEX_NAME",
searchParams = SearchParamsObject(
query = "peace",
),
requestOptions = RequestOptions(
headers = buildMap {
put("X-Algolia-User-ID", "user42")
},
),
)
|
1
2
3
4
5
6
7
8
9
10
| $response = $client->searchSingleIndex(
'ALGOLIA_INDEX_NAME',
['query' => 'peace',
],
[
'headers' => [
'X-Algolia-User-ID' => 'user42',
],
]
);
|
1
2
3
4
5
6
7
8
9
| response = client.search_single_index(
index_name="ALGOLIA_INDEX_NAME",
search_params={
"query": "peace",
},
request_options={
"headers": loads("""{"X-Algolia-User-ID":"user42"}"""),
},
)
|
1
2
3
4
5
| response = client.search_single_index(
"ALGOLIA_INDEX_NAME",
Algolia::Search::SearchParamsObject.new(query: "peace"),
{:header_params => {"X-Algolia-User-ID" => "user42"}}
)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| val response = Await.result(
client.searchSingleIndex(
indexName = "ALGOLIA_INDEX_NAME",
searchParams = Some(
SearchParamsObject(
query = Some("peace")
)
),
requestOptions = Some(
RequestOptions
.builder()
.withHeader("X-Algolia-User-ID", "user42")
.build()
)
),
Duration(100, "sec")
)
|
1
2
3
4
5
6
7
| let response: SearchResponse<Hit> = try await client.searchSingleIndex(
indexName: "ALGOLIA_INDEX_NAME",
searchParams: SearchSearchParams.searchSearchParamsObject(SearchSearchParamsObject(query: "peace")),
requestOptions: RequestOptions(
headers: ["X-Algolia-User-ID": "user42"]
)
)
|
Accessing secured data
You can’t rely on filters to select what data to return to a specific user because users can change these parameters.
If all you have to pick specific data is query time filters, anyone can alter these to access the data of another user.
To properly restrict access of a user to only their data (and the public data), you need to use secured API keys.
Without MCM, you need to set the filters to authorize manually.
1
2
3
4
| var response = client.GenerateSecuredApiKey(
"YourSearchOnlyApiKey",
new SecuredApiKeyRestrictions { Filters = "user:user42 AND user:public" }
);
|
1
2
3
4
5
6
| final response = client.generateSecuredApiKey(
parentApiKey: "YourSearchOnlyApiKey",
restrictions: SecuredApiKeyRestrictions(
filters: "user:user42 AND user:public",
),
);
|
1
2
3
4
5
6
7
| response, err := client.GenerateSecuredApiKey(
"YourSearchOnlyApiKey",
search.NewEmptySecuredApiKeyRestrictions().SetFilters("user:user42 AND user:public"))
if err != nil {
// handle the eventual error
panic(err)
}
|
1
| client.generateSecuredApiKey("YourSearchOnlyApiKey", new SecuredApiKeyRestrictions().setFilters("user:user42 AND user:public"));
|
1
2
3
4
| const response = client.generateSecuredApiKey({
parentApiKey: 'YourSearchOnlyApiKey',
restrictions: { filters: 'user:user42 AND user:public' },
});
|
1
2
3
4
5
6
| var response = client.generateSecuredApiKey(
parentApiKey = "YourSearchOnlyApiKey",
restrictions = SecuredApiKeyRestrictions(
filters = "user:user42 AND user:public",
),
)
|
1
2
3
4
5
| $response = $client->generateSecuredApiKey(
'YourSearchOnlyApiKey',
['filters' => 'user:user42 AND user:public',
],
);
|
1
2
3
4
5
6
| response = client.generate_secured_api_key(
parent_api_key="YourSearchOnlyApiKey",
restrictions={
"filters": "user:user42 AND user:public",
},
)
|
1
2
3
4
| response = client.generate_secured_api_key(
"YourSearchOnlyApiKey",
Algolia::Search::SecuredApiKeyRestrictions.new(filters: "user:user42 AND user:public")
)
|
1
2
3
4
5
6
| val response = client.generateSecuredApiKey(
parentApiKey = "YourSearchOnlyApiKey",
restrictions = SecuredApiKeyRestrictions(
filters = Some("user:user42 AND user:public")
)
)
|
1
2
3
4
| let response = try client.generateSecuredApiKey(
parentApiKey: "YourSearchOnlyApiKey",
restrictions: SecuredApiKeyRestrictions(filters: "user:user42 AND user:public")
)
|
With MCM, you still need to generate secured API keys, but all you have to do is provide the userToken
. It automatically adds the right filters to authorize.
1
2
3
4
| var response = client.GenerateSecuredApiKey(
"YourSearchOnlyApiKey",
new SecuredApiKeyRestrictions { UserToken = "user42" }
);
|
1
2
3
4
5
6
| final response = client.generateSecuredApiKey(
parentApiKey: "YourSearchOnlyApiKey",
restrictions: SecuredApiKeyRestrictions(
userToken: "user42",
),
);
|
1
2
3
4
5
6
7
| response, err := client.GenerateSecuredApiKey(
"YourSearchOnlyApiKey",
search.NewEmptySecuredApiKeyRestrictions().SetUserToken("user42"))
if err != nil {
// handle the eventual error
panic(err)
}
|
1
| client.generateSecuredApiKey("YourSearchOnlyApiKey", new SecuredApiKeyRestrictions().setUserToken("user42"));
|
1
2
3
4
| const response = client.generateSecuredApiKey({
parentApiKey: 'YourSearchOnlyApiKey',
restrictions: { userToken: 'user42' },
});
|
1
2
3
4
5
6
| var response = client.generateSecuredApiKey(
parentApiKey = "YourSearchOnlyApiKey",
restrictions = SecuredApiKeyRestrictions(
userToken = "user42",
),
)
|
1
2
3
4
5
| $response = $client->generateSecuredApiKey(
'YourSearchOnlyApiKey',
['userToken' => 'user42',
],
);
|
1
2
3
4
5
6
| response = client.generate_secured_api_key(
parent_api_key="YourSearchOnlyApiKey",
restrictions={
"userToken": "user42",
},
)
|
1
2
3
4
| response = client.generate_secured_api_key(
"YourSearchOnlyApiKey",
Algolia::Search::SecuredApiKeyRestrictions.new(user_token: "user42")
)
|
1
2
3
4
5
6
| val response = client.generateSecuredApiKey(
parentApiKey = "YourSearchOnlyApiKey",
restrictions = SecuredApiKeyRestrictions(
userToken = Some("user42")
)
)
|
1
2
3
4
| let response = try client.generateSecuredApiKey(
parentApiKey: "YourSearchOnlyApiKey",
restrictions: SecuredApiKeyRestrictions(userToken: "user42")
)
|
With MCM, you always need to provide the X-Algolia-User-ID
header, even when the query contains the userToken
parameter.
The header is required to route the requests efficiently.
Index configuration and API keys
When performing actions that have global impact on all clusters, such as using addApiKey
or setSettings
, you don’t need to provide the X-Algolia-User-ID
header.