Skip to content

Commit ca7c180

Browse files
Merge #430
430: Add Sync Indexes API r=curquiza a=Sherlouk # Pull Request ## Related issue Fixes #367 ## What does this PR do? - Introduces new swapIndexes API. This is NOT a breaking change. - Adds a new "wait" function to TaskInfo for cleaner async code (additive). Worth noting that the above issue references an "API key action" and an error code. Neither of these are currently stored in the package and thus no changes needed. ## PR checklist Please check if your PR fulfills the following requirements: - [x] Does this PR fix an existing issue, or have you listed the changes applied in the PR description (and why they are needed)? - [x] Have you read the contributing guidelines? - [x] Have you made sure that the title is accurate and descriptive of the changes? Thank you so much for contributing to Meilisearch! Co-authored-by: James Sherlock <[email protected]>
2 parents fa72806 + 8ac6987 commit ca7c180

File tree

7 files changed

+114
-11
lines changed

7 files changed

+114
-11
lines changed

.code-samples.meilisearch.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,3 +1575,8 @@ landing_getting_started_1: |-
15751575
print(error)
15761576
}
15771577
}
1578+
swap_indexes_1: |-
1579+
let task = try await self.client.swapIndexes([
1580+
("indexA", "indexB"),
1581+
("indexX", "indexY")
1582+
])

Sources/MeiliSearch/Async/Client+async.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,17 @@ extension MeiliSearch {
5757
}
5858
}
5959

60+
/**
61+
See `swapIndexes(_:_:)`
62+
*/
63+
public func swapIndexes(_ pairs: [(String, String)]) async throws -> TaskInfo {
64+
try await withCheckedThrowingContinuation { continuation in
65+
self.swapIndexes(pairs) { result in
66+
continuation.resume(with: result)
67+
}
68+
}
69+
}
70+
6071
/**
6172
See `waitForTask(taskUid:options:_:)`
6273
*/

Sources/MeiliSearch/Client.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,13 @@ public struct MeiliSearch {
129129
self.index(uid).delete(completion)
130130
}
131131

132-
// MARK: WAIT FOR TASK
132+
public func swapIndexes(
133+
_ pairs: [(String, String)],
134+
_ completion: @escaping (Result<TaskInfo, Swift.Error>) -> Void) {
135+
Indexes.swapIndexes(pairs: pairs, request: request, completion)
136+
}
137+
138+
// MARK: Wait for Task
133139

134140
/**
135141
Wait for a task to be successful or fail.

Sources/MeiliSearch/Indexes.swift

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,40 @@ public struct Indexes {
167167
}
168168
}
169169

170+
static func swapIndexes(
171+
pairs: [(old: String, new: String)],
172+
request: Request,
173+
_ completion: @escaping (Result<TaskInfo, Swift.Error>) -> Void) {
174+
175+
let swapIndexPayload: [SwapIndexPayload] = pairs.map {
176+
SwapIndexPayload(indexes: [ $0.old, $0.new ])
177+
}
178+
179+
let data: Data
180+
do {
181+
data = try JSONEncoder().encode(swapIndexPayload)
182+
} catch {
183+
completion(.failure(MeiliSearch.Error.invalidJSON))
184+
return
185+
}
186+
187+
request.post(api: "/swap-indexes", data) { result in
188+
switch result {
189+
case .success(let data):
190+
do {
191+
let taskInfo: TaskInfo = try Constants.customJSONDecoder.decode(
192+
TaskInfo.self,
193+
from: data)
194+
completion(.success(taskInfo))
195+
} catch {
196+
completion(.failure(error))
197+
}
198+
case .failure(let error):
199+
completion(.failure(error))
200+
}
201+
}
202+
}
203+
170204
/**
171205
Update the index primaryKey.
172206

@@ -1089,15 +1123,11 @@ public struct Indexes {
10891123
}
10901124

10911125
struct CreateIndexPayload: Codable {
1092-
public let uid: String
1093-
public let primaryKey: String?
1126+
let uid: String
1127+
let primaryKey: String?
1128+
}
10941129

1095-
public init(
1096-
uid: String,
1097-
primaryKey: String? = nil
1098-
) {
1099-
self.uid = uid
1100-
self.primaryKey = primaryKey
1101-
}
1130+
public struct SwapIndexPayload: Codable, Equatable {
1131+
public let indexes: [String]
11021132
}
11031133
}

Sources/MeiliSearch/Model/Task/TaskDetails.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ public extension Task {
8282
}
8383

8484
struct TaskIndexSwapDetails: Decodable, Equatable {
85-
// To be populated under https://github.com/meilisearch/meilisearch-swift/issues/367
85+
/// Object containing the payload for the indexSwap task
86+
public let swaps: [Indexes.SwapIndexPayload]
8687
}
8788

8889
struct TaskCancellationDetails: Decodable, Equatable {

Sources/MeiliSearch/Model/Task/TaskInfo.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,10 @@ public struct TaskInfo: Codable, Equatable {
2323
public enum CodingKeys: String, CodingKey {
2424
case taskUid, indexUid, status, type, enqueuedAt
2525
}
26+
27+
@discardableResult
28+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
29+
public func wait(on client: MeiliSearch, options: WaitOptions? = nil) async throws -> Task {
30+
try await client.waitForTask(task: self, options: options)
31+
}
2632
}

Tests/MeiliSearchIntegrationTests/IndexesTests.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,50 @@ class IndexesTests: XCTestCase {
243243
self.wait(for: [updateExpectation], timeout: TESTS_TIME_OUT)
244244
}
245245

246+
func testSwapIndex() async throws {
247+
let documents: [Movie] = [
248+
Movie(id: 123, title: "Pride and Prejudice", comment: "A great book"),
249+
Movie(id: 456, title: "Le Petit Prince", comment: "A french book"),
250+
]
251+
252+
// Remove indexes (if present)
253+
try await client.deleteIndex("indexA").wait(on: client)
254+
try await client.deleteIndex("indexB").wait(on: client)
255+
256+
// Create destination index
257+
try await client.createIndex(uid: "indexA").wait(on: client)
258+
259+
// Create source index
260+
try await client.createIndex(uid: "indexB").wait(on: client)
261+
try await client.index("indexB").addDocuments(documents: documents).wait(on: client)
262+
try await client.index("indexB").updateStopWords(["test"]).wait(on: client)
263+
264+
// Verify indexA (destination) does not have any stop words or documents
265+
let stopWords: [String] = try await client.index("indexA").getStopWords()
266+
XCTAssertEqual(stopWords, [])
267+
268+
let movies: DocumentsResults<Movie> = try await client.index("indexA").getDocuments()
269+
XCTAssertEqual(movies.total, 0)
270+
271+
// Replace indexes
272+
let task = try await client.swapIndexes([("indexA", "indexB")]).wait(on: client)
273+
XCTAssertEqual(task.type, .indexSwap)
274+
275+
guard case .indexSwap(let value) = task.details else {
276+
XCTFail("Task Not a Swap")
277+
return
278+
}
279+
280+
XCTAssertEqual(value.swaps[0], .init(indexes: ["indexA", "indexB"]))
281+
282+
// Verify indexA (now source) does have stop words and documents
283+
let stopWordsNew: [String] = try await client.index("indexA").getStopWords()
284+
XCTAssertEqual(stopWordsNew, ["test"])
285+
286+
let moviesNew: DocumentsResults<Movie> = try await client.index("indexA").getDocuments()
287+
XCTAssertEqual(moviesNew.total, 2)
288+
}
289+
246290
func testDeleteIndex() {
247291

248292
let createExpectation = XCTestExpectation(description: "Create Movies index")

0 commit comments

Comments
 (0)