@@ -22,6 +22,7 @@ It also provides the `crud-storage` and `crud-router` roles for
2222 - [ Delete] ( #delete )
2323 - [ Replace] ( #replace )
2424 - [ Upsert] ( #upsert )
25+ - [ Upsert many] ( #upsert-many )
2526 - [ Select] ( #select )
2627 - [ Select conditions] ( #select-conditions )
2728 - [ Pairs] ( #pairs )
@@ -567,6 +568,134 @@ crud.upsert_object('customers',
567568...
568569```
569570
571+ ### Upsert many
572+
573+ ``` lua
574+ -- Upsert batch of tuples
575+ local result , err = crud .upsert_many (space_name , tuples_operation_data , opts )
576+ -- Upsert batch of objects
577+ local result , err = crud .upsert_object_many (space_name , objects_operation_data , opts )
578+ ```
579+
580+ where:
581+
582+ * ` space_name ` (` string ` ) - name of the space to insert an object
583+ * ` tuples_operation_data ` / ` objects_operation_data ` (` table ` ) - array of
584+ tuples/objects to insert
585+ and update [ operations] ( https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/#box-space-update )
586+ in format {{tuple_1, operation_1}, ..., {tuple_n, operation_n}},
587+ if there is tuple with duplicate key then existing tuple will
588+ be updated with update operations
589+ * ` opts ` :
590+ * ` timeout ` (` ?number ` ) - ` vshard.call ` timeout (in seconds)
591+ * ` fields ` (` ?table ` ) - field names for getting only a subset of fields
592+ * ` stop_on_error ` (` ?boolean ` ) - stop on a first error and report error
593+ regarding the failed operation and error about what tuples were not
594+ performed, default is ` false `
595+ * ` rollback_on_error ` (` ?boolean ` ) - any failed operation will lead to
596+ rollback on a storage, where the operation is failed, report error
597+ about what tuples were rollback, default is ` false `
598+
599+ Returns metadata and array of errors.
600+ Each error object can contain field ` operation_data ` .
601+
602+ ` operation_data ` field can contain:
603+ * tuple for which the error occurred;
604+ * object with an incorrect format;
605+ * tuple the operation on which was performed but
606+ operation was rollback;
607+ * tuple the operation on which was not performed
608+ because operation was stopped by error.
609+
610+ Right now CRUD cannot provide batch upsert with full consistency.
611+ CRUD offers batch upsert with partial consistency. That means
612+ that full consistency can be provided only on single replicaset
613+ using ` box ` transactions.
614+
615+ ** Example:**
616+
617+ ``` lua
618+ crud .upsert_many (' customers' , {
619+ {{1 , box .NULL , ' Elizabeth' , 23 }, {{' +' , ' age' , 1 }}},
620+ {{2 , box .NULL , ' Anastasia' , 22 }, {{' +' , ' age' , 2 }, {' =' , ' name' , ' Oleg' }}}
621+ })
622+ ---
623+ - metadata :
624+ - {' name' : ' id' , ' type' : ' unsigned' }
625+ - {' name' : ' bucket_id' , ' type' : ' unsigned' }
626+ - {' name' : ' name' , ' type' : ' string' }
627+ - {' name' : ' age' , ' type' : ' number' }
628+
629+ ...
630+ crud .upsert_object_many (' customers' , {
631+ {{id = 3 , name = ' Elizabeth' , age = 24 }, {{' +' , ' age' , 1 }}},
632+ {{id = 10 , name = ' Anastasia' , age = 21 }, {{' +' , ' age' , 2 }}}
633+ })
634+ ---
635+ - metadata :
636+ - {' name' : ' id' , ' type' : ' unsigned' }
637+ - {' name' : ' bucket_id' , ' type' : ' unsigned' }
638+ - {' name' : ' name' , ' type' : ' string' }
639+ - {' name' : ' age' , ' type' : ' number' }
640+
641+ -- Partial success
642+ local res , errs = crud .upsert_object_many (' customers' , {
643+ {{id = 22 , name = ' Alex' , age = 34 }, {{' +' , ' age' , 12 }}},
644+ {{id = 3 , name = ' Anastasia' , age = 22 }, {{' =' , ' age' , ' invalid type' }}},
645+ {{id = 5 , name = ' Sergey' , age = 25 }, {{' +' , ' age' , 10 }}}
646+ })
647+ ---
648+ res
649+ - metadata :
650+ - {' name' : ' id' , ' type' : ' unsigned' }
651+ - {' name' : ' bucket_id' , ' type' : ' unsigned' }
652+ - {' name' : ' name' , ' type' : ' string' }
653+ - {' name' : ' age' , ' type' : ' number' }
654+
655+ # errs -- 1
656+ errs [1 ].class_name -- BatchUpsertError
657+ errs [1 ].err -- 'Tuple field 4 (age) type does not match one required by operation <...>'
658+ errs [1 ].tuple -- {3, 2804, 'Anastasia', 22}
659+ ...
660+ -- Partial success success with stop and rollback on error
661+ -- stop_on_error = true, rollback_on_error = true
662+ -- two error on one storage with rollback,
663+ -- inserts stop by error on this storage
664+ -- inserts before error are rollback
665+ local res , errs = crud .upsert_object_many (' customers' , {
666+ {{id = 6 , name = ' Alex' , age = 34 }, {{' +' , ' age' , 1 }}},
667+ {{id = 92 , name = ' Artur' , age = 29 }, {{' +' , ' age' , 2 }}},
668+ {{id = 3 , name = ' Anastasia' , age = 22 }, {{' +' , ' age' , ' 3' }}},
669+ {{id = 4 , name = ' Sergey' , age = 25 }, {{' +' , ' age' , 4 }}},
670+ {{id = 9 , name = ' Anna' , age = 30 }, {{' +' , ' age' , 5 }}},
671+ {{id = 71 , name = ' Oksana' , age = 29 }, {{' +' , ' age' , ' 6' }}},
672+ }, {
673+ stop_on_error = true ,
674+ rollback_on_error = true ,
675+ })
676+ res
677+ - metadata :
678+ - {' name' : ' id' , ' type' : ' unsigned' }
679+ - {' name' : ' bucket_id' , ' type' : ' unsigned' }
680+ - {' name' : ' name' , ' type' : ' string' }
681+ - {' name' : ' age' , ' type' : ' number' }
682+ # errs -- 4
683+ errs [1 ].class_name -- UpsertManyError
684+ errs [1 ].err -- 'Duplicate key exists <...>'
685+ errs [1 ].tuple -- {3, 2804, 'Anastasia', 22}
686+
687+ errs [2 ].class_name -- NotPerformedError
688+ errs [2 ].err -- 'Operation with tuple was not performed'
689+ errs [2 ].tuple -- {9, 1644, "Anna", 30}
690+
691+ errs [3 ].class_name -- NotPerformedError
692+ errs [3 ].err -- 'Operation with tuple was not performed'
693+ errs [3 ].tuple -- {71, 1802, "Oksana", 29}
694+
695+ errs [4 ].class_name -- NotPerformedError
696+ errs [4 ].err -- 'Operation with tuple was rollback'
697+ errs [4 ].tuple -- {92, 2040, "Artur", 29}
698+ ```
570699
571700### Select
572701
0 commit comments