mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-09-06 04:36:32 +00:00
Provide a rename argument to the swap
This commit is contained in:
@ -4,7 +4,7 @@ use deserr::actix_web::AwebJson;
|
||||
use deserr::Deserr;
|
||||
use index_scheduler::IndexScheduler;
|
||||
use meilisearch_types::deserr::DeserrJsonError;
|
||||
use meilisearch_types::error::deserr_codes::InvalidSwapIndexes;
|
||||
use meilisearch_types::error::deserr_codes::{InvalidSwapIndexes, InvalidSwapRename};
|
||||
use meilisearch_types::error::ResponseError;
|
||||
use meilisearch_types::index_uid::IndexUid;
|
||||
use meilisearch_types::tasks::{IndexSwap, KindWithContent};
|
||||
@ -33,11 +33,15 @@ pub struct SwapIndexesPayload {
|
||||
/// Array of the two indexUids to be swapped
|
||||
#[deserr(error = DeserrJsonError<InvalidSwapIndexes>, missing_field_error = DeserrJsonError::missing_swap_indexes)]
|
||||
indexes: Vec<IndexUid>,
|
||||
/// If set to true, instead of swapping the left and right indexes it'll change the name of the first index to the second
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSwapRename>)]
|
||||
rename: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct IndexSwappedAnalytics {
|
||||
swap_operation_number: usize,
|
||||
rename_used: bool,
|
||||
}
|
||||
|
||||
impl Aggregate for IndexSwappedAnalytics {
|
||||
@ -48,6 +52,7 @@ impl Aggregate for IndexSwappedAnalytics {
|
||||
fn aggregate(self: Box<Self>, new: Box<Self>) -> Box<Self> {
|
||||
Box::new(Self {
|
||||
swap_operation_number: self.swap_operation_number.max(new.swap_operation_number),
|
||||
rename_used: self.rename_used | new.rename_used,
|
||||
})
|
||||
}
|
||||
|
||||
@ -95,11 +100,17 @@ pub async fn swap_indexes(
|
||||
analytics: web::Data<Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let params = params.into_inner();
|
||||
analytics.publish(IndexSwappedAnalytics { swap_operation_number: params.len() }, &req);
|
||||
analytics.publish(
|
||||
IndexSwappedAnalytics {
|
||||
swap_operation_number: params.len(),
|
||||
rename_used: params.iter().any(|obj| obj.rename),
|
||||
},
|
||||
&req,
|
||||
);
|
||||
let filters = index_scheduler.filters();
|
||||
|
||||
let mut swaps = vec![];
|
||||
for SwapIndexesPayload { indexes } in params.into_iter() {
|
||||
for SwapIndexesPayload { indexes, rename } in params.into_iter() {
|
||||
// TODO: switch to deserr
|
||||
let (lhs, rhs) = match indexes.as_slice() {
|
||||
[lhs, rhs] => (lhs, rhs),
|
||||
@ -110,7 +121,7 @@ pub async fn swap_indexes(
|
||||
if !filters.is_index_authorized(lhs) || !filters.is_index_authorized(rhs) {
|
||||
return Err(AuthenticationError::InvalidToken.into());
|
||||
}
|
||||
swaps.push(IndexSwap { indexes: (lhs.to_string(), rhs.to_string()) });
|
||||
swaps.push(IndexSwap { indexes: (lhs.to_string(), rhs.to_string()), rename });
|
||||
}
|
||||
|
||||
let task = KindWithContent::IndexSwap { swaps };
|
||||
|
@ -92,3 +92,20 @@ async fn swap_indexes_bad_indexes() {
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn swap_indexes_bad_rename() {
|
||||
let server = Server::new_shared();
|
||||
|
||||
let (response, code) =
|
||||
server.index_swap(json!([{ "indexes": ["kefir", "intel"], "rename": "hello" }])).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r#"
|
||||
{
|
||||
"message": "Invalid value type at `[0].rename`: expected a boolean, but found a string: `\"hello\"`",
|
||||
"code": "invalid_swap_rename",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_swap_rename"
|
||||
}
|
||||
"#);
|
||||
}
|
||||
|
@ -372,3 +372,255 @@ async fn swap_indexes() {
|
||||
let (res, _) = d.get_all_documents(GetAllDocumentsOptions::default()).await;
|
||||
snapshot!(res["results"], @r###"[{"id":1,"index":"c"}]"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn swap_rename_indexes() {
|
||||
let server = Server::new().await;
|
||||
let a = server.index("a");
|
||||
let b = server.index("b");
|
||||
a.create(None).await;
|
||||
a.add_documents(json!({ "id": 1, "index": "a"}), None).await;
|
||||
|
||||
let (res, _code) = server.index_swap(json!([{ "indexes": ["a", "b"], "rename": true }])).await;
|
||||
server.wait_task(res.uid()).await.succeeded();
|
||||
|
||||
let (tasks, _code) = server.tasks().await;
|
||||
|
||||
// Notice how the task 0 which was initially representing the creation of the index `A` now represents the creation of the index `B`.
|
||||
snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r#"
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uid": 2,
|
||||
"batchUid": 2,
|
||||
"indexUid": null,
|
||||
"status": "succeeded",
|
||||
"type": "indexSwap",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"swaps": [
|
||||
{
|
||||
"indexes": [
|
||||
"a",
|
||||
"b"
|
||||
],
|
||||
"rename": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 1,
|
||||
"batchUid": 1,
|
||||
"indexUid": "b",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 0,
|
||||
"batchUid": 0,
|
||||
"indexUid": "b",
|
||||
"status": "succeeded",
|
||||
"type": "indexCreation",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"primaryKey": null
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
}
|
||||
],
|
||||
"total": 3,
|
||||
"limit": 20,
|
||||
"from": 2,
|
||||
"next": null
|
||||
}
|
||||
"#);
|
||||
|
||||
// BUT, `a` should not exists
|
||||
let (res, code) = a.get_all_documents(GetAllDocumentsOptions::default()).await;
|
||||
snapshot!(code, @"404 Not Found");
|
||||
snapshot!(res["results"], @"null");
|
||||
|
||||
// And its data should be in b
|
||||
let (res, code) = b.get_all_documents(GetAllDocumentsOptions::default()).await;
|
||||
snapshot!(code, @"200 OK");
|
||||
snapshot!(res["results"], @r#"[{"id":1,"index":"a"}]"#);
|
||||
|
||||
// No tasks should be linked to the index a
|
||||
let (tasks, _code) = server.tasks_filter("indexUids=a").await;
|
||||
snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r#"
|
||||
{
|
||||
"results": [],
|
||||
"total": 1,
|
||||
"limit": 20,
|
||||
"from": null,
|
||||
"next": null
|
||||
}
|
||||
"#);
|
||||
|
||||
// They should be linked to the index b
|
||||
let (tasks, _code) = server.tasks_filter("indexUids=b").await;
|
||||
snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r#"
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uid": 1,
|
||||
"batchUid": 1,
|
||||
"indexUid": "b",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 0,
|
||||
"batchUid": 0,
|
||||
"indexUid": "b",
|
||||
"status": "succeeded",
|
||||
"type": "indexCreation",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"primaryKey": null
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
}
|
||||
],
|
||||
"total": 3,
|
||||
"limit": 20,
|
||||
"from": 1,
|
||||
"next": null
|
||||
}
|
||||
"#);
|
||||
|
||||
// ===== Now, we can delete the index `b`, but its tasks will stays
|
||||
// if we then make a new `b` index and rename it to be called `a`
|
||||
// the tasks currently available in `b` should not be moved
|
||||
|
||||
let (res, _) = b.delete().await;
|
||||
server.wait_task(res.uid()).await.succeeded();
|
||||
|
||||
let (res, _) = b.create(Some("kefir")).await;
|
||||
let (res, _code) = server.index_swap(json!([{ "indexes": ["b", "a"], "rename": true }])).await;
|
||||
server.wait_task(res.uid()).await.succeeded();
|
||||
|
||||
// `a` now contains everything
|
||||
let (tasks, _code) = server.tasks_filter("indexUids=a").await;
|
||||
snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r#"
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uid": 4,
|
||||
"batchUid": 4,
|
||||
"indexUid": "a",
|
||||
"status": "succeeded",
|
||||
"type": "indexCreation",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"primaryKey": "kefir"
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 3,
|
||||
"batchUid": 3,
|
||||
"indexUid": "a",
|
||||
"status": "succeeded",
|
||||
"type": "indexDeletion",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"deletedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 1,
|
||||
"batchUid": 1,
|
||||
"indexUid": "a",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 0,
|
||||
"batchUid": 0,
|
||||
"indexUid": "a",
|
||||
"status": "succeeded",
|
||||
"type": "indexCreation",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"primaryKey": null
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
}
|
||||
],
|
||||
"total": 6,
|
||||
"limit": 20,
|
||||
"from": 4,
|
||||
"next": null
|
||||
}
|
||||
"#);
|
||||
|
||||
// And `b` is empty
|
||||
let (tasks, _code) = server.tasks_filter("indexUids=b").await;
|
||||
snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r#"
|
||||
{
|
||||
"results": [],
|
||||
"total": 2,
|
||||
"limit": 20,
|
||||
"from": null,
|
||||
"next": null
|
||||
}
|
||||
"#);
|
||||
}
|
||||
|
Reference in New Issue
Block a user