diff --git a/v2/arangodb/collection_documents_create.go b/v2/arangodb/collection_documents_create.go index 53d4c914..4e81f2c2 100644 --- a/v2/arangodb/collection_documents_create.go +++ b/v2/arangodb/collection_documents_create.go @@ -71,7 +71,9 @@ type CollectionDocumentCreate interface { } type CollectionDocumentCreateResponseReader interface { + shared.ReadAllReadable[CollectionDocumentCreateResponse] Read() (CollectionDocumentCreateResponse, error) + Len() int } type CollectionDocumentCreateResponse struct { diff --git a/v2/arangodb/collection_documents_create_impl.go b/v2/arangodb/collection_documents_create_impl.go index b8117243..a2874185 100644 --- a/v2/arangodb/collection_documents_create_impl.go +++ b/v2/arangodb/collection_documents_create_impl.go @@ -133,6 +133,7 @@ func newCollectionDocumentCreateResponseReader(array *connection.Array, options c.response.New = newUnmarshalInto(c.options.NewObject) } + c.ReadAllReader = shared.ReadAllReader[CollectionDocumentCreateResponse, *collectionDocumentCreateResponseReader]{Reader: c} return c } @@ -147,6 +148,12 @@ type collectionDocumentCreateResponseReader struct { Old *UnmarshalInto `json:"old,omitempty"` New *UnmarshalInto `json:"new,omitempty"` } + shared.ReadAllReader[CollectionDocumentCreateResponse, *collectionDocumentCreateResponseReader] + + // Cache for len() method + cachedResults []CollectionDocumentCreateResponse + cachedErrors []error + cached bool } func (c *collectionDocumentCreateResponseReader) Read() (CollectionDocumentCreateResponse, error) { @@ -171,9 +178,24 @@ func (c *collectionDocumentCreateResponseReader) Read() (CollectionDocumentCreat return CollectionDocumentCreateResponse{}, err } + // Update meta with the unmarshaled data + meta.DocumentMeta = *c.response.DocumentMeta + meta.ResponseStruct = *c.response.ResponseStruct + meta.Old = c.response.Old + meta.New = c.response.New + if meta.Error != nil && *meta.Error { return meta, meta.AsArangoError() } return meta, nil } + +// Len returns the number of items in the response +func (c *collectionDocumentCreateResponseReader) Len() int { + if !c.cached { + c.cachedResults, c.cachedErrors = c.ReadAll() + c.cached = true + } + return len(c.cachedResults) +} diff --git a/v2/arangodb/collection_documents_delete.go b/v2/arangodb/collection_documents_delete.go index 28641243..4a4e241c 100644 --- a/v2/arangodb/collection_documents_delete.go +++ b/v2/arangodb/collection_documents_delete.go @@ -63,7 +63,9 @@ type CollectionDocumentDeleteResponse struct { } type CollectionDocumentDeleteResponseReader interface { + shared.ReadAllIntoReadable[CollectionDocumentDeleteResponse] Read(i interface{}) (CollectionDocumentDeleteResponse, error) + Len() int } type CollectionDocumentDeleteOptions struct { @@ -82,6 +84,7 @@ type CollectionDocumentDeleteOptions struct { WithWaitForSync *bool // Return additionally the complete previous revision of the changed document + // Should be a pointer to an object OldObject interface{} // If set to true, an empty object is returned as response if the document operation succeeds. diff --git a/v2/arangodb/collection_documents_delete_impl.go b/v2/arangodb/collection_documents_delete_impl.go index 9a12837e..54248833 100644 --- a/v2/arangodb/collection_documents_delete_impl.go +++ b/v2/arangodb/collection_documents_delete_impl.go @@ -24,6 +24,7 @@ import ( "context" "io" "net/http" + "reflect" "github.com/pkg/errors" @@ -42,6 +43,7 @@ var _ CollectionDocumentDelete = &collectionDocumentDelete{} type collectionDocumentDelete struct { collection *collection + shared.ReadAllIntoReader[CollectionDocumentDeleteResponse, *collectionDocumentDeleteResponseReader] } func (c collectionDocumentDelete) DeleteDocument(ctx context.Context, key string) (CollectionDocumentDeleteResponse, error) { @@ -103,6 +105,7 @@ func (c collectionDocumentDelete) DeleteDocumentsWithOptions(ctx context.Context func newCollectionDocumentDeleteResponseReader(array *connection.Array, options *CollectionDocumentDeleteOptions) *collectionDocumentDeleteResponseReader { c := &collectionDocumentDeleteResponseReader{array: array, options: options} + c.ReadAllIntoReader = shared.ReadAllIntoReader[CollectionDocumentDeleteResponse, *collectionDocumentDeleteResponseReader]{Reader: c} return c } @@ -111,6 +114,11 @@ var _ CollectionDocumentDeleteResponseReader = &collectionDocumentDeleteResponse type collectionDocumentDeleteResponseReader struct { array *connection.Array options *CollectionDocumentDeleteOptions + shared.ReadAllIntoReader[CollectionDocumentDeleteResponse, *collectionDocumentDeleteResponseReader] + // Cache for len() method + cachedResults []CollectionDocumentDeleteResponse + cachedErrors []error + cached bool } func (c *collectionDocumentDeleteResponseReader) Read(i interface{}) (CollectionDocumentDeleteResponse, error) { @@ -146,11 +154,30 @@ func (c *collectionDocumentDeleteResponseReader) Read(i interface{}) (Collection } if c.options != nil && c.options.OldObject != nil { - meta.Old = c.options.OldObject + // Create a new instance for each document to avoid reusing the same pointer + oldObjectType := reflect.TypeOf(c.options.OldObject).Elem() + meta.Old = reflect.New(oldObjectType).Interface() + + // Extract old data into the new instance if err := response.Object.Object.Extract("old").Inject(meta.Old); err != nil { return CollectionDocumentDeleteResponse{}, err } + + // Copy data from the new instance to the original OldObject for backward compatibility + oldValue := reflect.ValueOf(meta.Old).Elem() + originalValue := reflect.ValueOf(c.options.OldObject).Elem() + originalValue.Set(oldValue) } return meta, nil } + +// Len returns the number of items in the response +func (c *collectionDocumentDeleteResponseReader) Len() int { + if !c.cached { + var dummySlice []interface{} + c.cachedResults, c.cachedErrors = c.ReadAll(&dummySlice) + c.cached = true + } + return len(c.cachedResults) +} diff --git a/v2/arangodb/collection_documents_read.go b/v2/arangodb/collection_documents_read.go index 32c49d83..f46d40f8 100644 --- a/v2/arangodb/collection_documents_read.go +++ b/v2/arangodb/collection_documents_read.go @@ -59,6 +59,8 @@ type CollectionDocumentRead interface { type CollectionDocumentReadResponseReader interface { Read(i interface{}) (CollectionDocumentReadResponse, error) + shared.ReadAllIntoReadable[CollectionDocumentReadResponse] + Len() int } type CollectionDocumentReadResponse struct { diff --git a/v2/arangodb/collection_documents_read_impl.go b/v2/arangodb/collection_documents_read_impl.go index 6a30bfb8..8bc680cc 100644 --- a/v2/arangodb/collection_documents_read_impl.go +++ b/v2/arangodb/collection_documents_read_impl.go @@ -97,7 +97,7 @@ func (c collectionDocumentRead) ReadDocumentWithOptions(ctx context.Context, key func newCollectionDocumentReadResponseReader(array *connection.Array, options *CollectionDocumentReadOptions) *collectionDocumentReadResponseReader { c := &collectionDocumentReadResponseReader{array: array, options: options} - + c.ReadAllIntoReader = shared.ReadAllIntoReader[CollectionDocumentReadResponse, *collectionDocumentReadResponseReader]{Reader: c} return c } @@ -106,6 +106,11 @@ var _ CollectionDocumentReadResponseReader = &collectionDocumentReadResponseRead type collectionDocumentReadResponseReader struct { array *connection.Array options *CollectionDocumentReadOptions + shared.ReadAllIntoReader[CollectionDocumentReadResponse, *collectionDocumentReadResponseReader] + // Cache for len() method + cachedResults []CollectionDocumentReadResponse + cachedErrors []error + cached bool } func (c *collectionDocumentReadResponseReader) Read(i interface{}) (CollectionDocumentReadResponse, error) { @@ -142,3 +147,13 @@ func (c *collectionDocumentReadResponseReader) Read(i interface{}) (CollectionDo return meta, nil } + +// Len returns the number of items in the response +func (c *collectionDocumentReadResponseReader) Len() int { + if !c.cached { + var dummySlice []interface{} + c.cachedResults, c.cachedErrors = c.ReadAll(&dummySlice) + c.cached = true + } + return len(c.cachedResults) +} diff --git a/v2/arangodb/collection_documents_replace.go b/v2/arangodb/collection_documents_replace.go index 1e62b789..ed6a7269 100644 --- a/v2/arangodb/collection_documents_replace.go +++ b/v2/arangodb/collection_documents_replace.go @@ -61,7 +61,9 @@ type CollectionDocumentReplace interface { } type CollectionDocumentReplaceResponseReader interface { + shared.ReadAllReadable[CollectionDocumentReplaceResponse] Read() (CollectionDocumentReplaceResponse, error) + Len() int } type CollectionDocumentReplaceResponse struct { diff --git a/v2/arangodb/collection_documents_replace_impl.go b/v2/arangodb/collection_documents_replace_impl.go index 9da6ac36..9c138a40 100644 --- a/v2/arangodb/collection_documents_replace_impl.go +++ b/v2/arangodb/collection_documents_replace_impl.go @@ -24,6 +24,7 @@ import ( "context" "io" "net/http" + "reflect" "github.com/pkg/errors" @@ -132,7 +133,7 @@ func newCollectionDocumentReplaceResponseReader(array *connection.Array, options c.response.Old = newUnmarshalInto(c.options.OldObject) c.response.New = newUnmarshalInto(c.options.NewObject) } - + c.ReadAllReader = shared.ReadAllReader[CollectionDocumentReplaceResponse, *collectionDocumentReplaceResponseReader]{Reader: c} return c } @@ -147,6 +148,12 @@ type collectionDocumentReplaceResponseReader struct { Old *UnmarshalInto `json:"old,omitempty"` New *UnmarshalInto `json:"new,omitempty"` } + shared.ReadAllReader[CollectionDocumentReplaceResponse, *collectionDocumentReplaceResponseReader] + + // Cache for len() method + cachedResults []CollectionDocumentReplaceResponse + cachedErrors []error + cached bool } func (c *collectionDocumentReplaceResponseReader) Read() (CollectionDocumentReplaceResponse, error) { @@ -157,8 +164,15 @@ func (c *collectionDocumentReplaceResponseReader) Read() (CollectionDocumentRepl var meta CollectionDocumentReplaceResponse if c.options != nil { - meta.Old = c.options.OldObject - meta.New = c.options.NewObject + // Create new instances for each document to avoid reusing the same pointers + if c.options.OldObject != nil { + oldObjectType := reflect.TypeOf(c.options.OldObject).Elem() + meta.Old = reflect.New(oldObjectType).Interface() + } + if c.options.NewObject != nil { + newObjectType := reflect.TypeOf(c.options.NewObject).Elem() + meta.New = reflect.New(newObjectType).Interface() + } } c.response.DocumentMetaWithOldRev = &meta.DocumentMetaWithOldRev @@ -171,9 +185,24 @@ func (c *collectionDocumentReplaceResponseReader) Read() (CollectionDocumentRepl return CollectionDocumentReplaceResponse{}, err } + // Update meta with the unmarshaled data + meta.DocumentMetaWithOldRev = *c.response.DocumentMetaWithOldRev + meta.ResponseStruct = *c.response.ResponseStruct + meta.Old = c.response.Old + meta.New = c.response.New + if meta.Error != nil && *meta.Error { return meta, meta.AsArangoError() } return meta, nil } + +// Len returns the number of items in the response +func (c *collectionDocumentReplaceResponseReader) Len() int { + if !c.cached { + c.cachedResults, c.cachedErrors = c.ReadAll() + c.cached = true + } + return len(c.cachedResults) +} diff --git a/v2/arangodb/collection_documents_update.go b/v2/arangodb/collection_documents_update.go index adffcf6b..d565b420 100644 --- a/v2/arangodb/collection_documents_update.go +++ b/v2/arangodb/collection_documents_update.go @@ -62,7 +62,9 @@ type CollectionDocumentUpdate interface { } type CollectionDocumentUpdateResponseReader interface { + shared.ReadAllReadable[CollectionDocumentUpdateResponse] Read() (CollectionDocumentUpdateResponse, error) + Len() int } type CollectionDocumentUpdateResponse struct { diff --git a/v2/arangodb/collection_documents_update_impl.go b/v2/arangodb/collection_documents_update_impl.go index 068d1b39..23f57104 100644 --- a/v2/arangodb/collection_documents_update_impl.go +++ b/v2/arangodb/collection_documents_update_impl.go @@ -24,6 +24,7 @@ import ( "context" "io" "net/http" + "reflect" "github.com/pkg/errors" @@ -132,7 +133,7 @@ func newCollectionDocumentUpdateResponseReader(array *connection.Array, options c.response.Old = newUnmarshalInto(c.options.OldObject) c.response.New = newUnmarshalInto(c.options.NewObject) } - + c.ReadAllReader = shared.ReadAllReader[CollectionDocumentUpdateResponse, *collectionDocumentUpdateResponseReader]{Reader: c} return c } @@ -147,6 +148,12 @@ type collectionDocumentUpdateResponseReader struct { Old *UnmarshalInto `json:"old,omitempty"` New *UnmarshalInto `json:"new,omitempty"` } + shared.ReadAllReader[CollectionDocumentUpdateResponse, *collectionDocumentUpdateResponseReader] + + // Cache for len() method + cachedResults []CollectionDocumentUpdateResponse + cachedErrors []error + cached bool } func (c *collectionDocumentUpdateResponseReader) Read() (CollectionDocumentUpdateResponse, error) { @@ -157,8 +164,15 @@ func (c *collectionDocumentUpdateResponseReader) Read() (CollectionDocumentUpdat var meta CollectionDocumentUpdateResponse if c.options != nil { - meta.Old = c.options.OldObject - meta.New = c.options.NewObject + // Create new instances for each document to avoid reusing the same pointers + if c.options.OldObject != nil { + oldObjectType := reflect.TypeOf(c.options.OldObject).Elem() + meta.Old = reflect.New(oldObjectType).Interface() + } + if c.options.NewObject != nil { + newObjectType := reflect.TypeOf(c.options.NewObject).Elem() + meta.New = reflect.New(newObjectType).Interface() + } } c.response.DocumentMetaWithOldRev = &meta.DocumentMetaWithOldRev @@ -171,9 +185,24 @@ func (c *collectionDocumentUpdateResponseReader) Read() (CollectionDocumentUpdat return CollectionDocumentUpdateResponse{}, err } + // Update meta with the unmarshaled data + meta.DocumentMetaWithOldRev = *c.response.DocumentMetaWithOldRev + meta.ResponseStruct = *c.response.ResponseStruct + meta.Old = c.response.Old + meta.New = c.response.New + if meta.Error != nil && *meta.Error { return meta, meta.AsArangoError() } return meta, nil } + +// Len returns the number of items in the response +func (c *collectionDocumentUpdateResponseReader) Len() int { + if !c.cached { + c.cachedResults, c.cachedErrors = c.ReadAll() + c.cached = true + } + return len(c.cachedResults) +} diff --git a/v2/arangodb/shared/read_all.go b/v2/arangodb/shared/read_all.go new file mode 100644 index 00000000..777a37e6 --- /dev/null +++ b/v2/arangodb/shared/read_all.go @@ -0,0 +1,97 @@ +// DISCLAIMER +// +// # Copyright 2020-2025 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany + +package shared + +import ( + "errors" + "reflect" +) + +type readReader[T any] interface { + Read() (T, error) +} + +type ReadAllReadable[T any] interface { + ReadAll() ([]T, []error) +} + +type ReadAllReader[T any, R readReader[T]] struct { + Reader R +} + +func (r ReadAllReader[T, R]) ReadAll() ([]T, []error) { + var docs []T + var errs []error + for { + doc, e := r.Reader.Read() + if errors.Is(e, NoMoreDocumentsError{}) { + break + } + errs = append(errs, e) + docs = append(docs, doc) + } + return docs, errs +} + +type readReaderInto[T any] interface { + Read(i interface{}) (T, error) +} + +type ReadAllIntoReadable[T any] interface { + ReadAll(i interface{}) ([]T, []error) +} + +type ReadAllIntoReader[T any, R readReaderInto[T]] struct { + Reader R +} + +func (r ReadAllIntoReader[T, R]) ReadAll(i interface{}) ([]T, []error) { + + iVal := reflect.ValueOf(i) + if iVal.Kind() != reflect.Ptr || iVal.Elem().Kind() != reflect.Slice { + panic("i must be a pointer to a slice") + } + + eVal := iVal.Elem() + eType := eVal.Type().Elem() + + var docs []T + var errs []error + + for { + res := reflect.New(eType) + doc, e := r.Reader.Read(res.Interface()) + if errors.Is(e, NoMoreDocumentsError{}) { + break + } + + iDocVal := reflect.ValueOf(doc) + if iDocVal.Kind() == reflect.Ptr { + iDocVal = iDocVal.Elem() + } + docCopy := reflect.New(iDocVal.Type()).Elem() + docCopy.Set(iDocVal) + + errs = append(errs, e) + docs = append(docs, docCopy.Interface().(T)) + eVal = reflect.Append(eVal, res.Elem()) + } + iVal.Elem().Set(eVal) + return docs, errs +} diff --git a/v2/tests/database_collection_doc_create_code_test.go b/v2/tests/database_collection_doc_create_code_test.go index a59181ca..19d97fc1 100644 --- a/v2/tests/database_collection_doc_create_code_test.go +++ b/v2/tests/database_collection_doc_create_code_test.go @@ -24,11 +24,10 @@ import ( "context" "testing" - "github.com/arangodb/go-driver/v2/arangodb/shared" - "github.com/stretchr/testify/require" "github.com/arangodb/go-driver/v2/arangodb" + "github.com/arangodb/go-driver/v2/arangodb/shared" ) type DocWithCode struct { @@ -38,6 +37,7 @@ type DocWithCode struct { func Test_DatabaseCollectionDocCreateCode(t *testing.T) { Wrap(t, func(t *testing.T, client arangodb.Client) { + // COMMENTED OUT FOR DEBUGGING ONLY WithDatabase(t, client, nil, func(db arangodb.Database) { WithCollectionV2(t, db, nil, func(col arangodb.Collection) { withContextT(t, defaultTestTimeout, func(ctx context.Context, tb testing.TB) { @@ -85,7 +85,7 @@ func Test_DatabaseCollectionDocCreateCode(t *testing.T) { meta, err := docs.Read(&z) require.NoError(t, err) - require.EqualValues(t, "test", meta.Key) + require.Equal(t, "test", meta.Key) _, err = docs.Read(&z) require.Error(t, err) @@ -93,7 +93,7 @@ func Test_DatabaseCollectionDocCreateCode(t *testing.T) { meta, err = docs.Read(&z) require.NoError(t, err) - require.EqualValues(t, "test2", meta.Key) + require.Equal(t, "test2", meta.Key) _, err = docs.Read(&z) require.Error(t, err) @@ -101,5 +101,54 @@ func Test_DatabaseCollectionDocCreateCode(t *testing.T) { }) }) }) + + WithDatabase(t, client, nil, func(db arangodb.Database) { + WithCollectionV2(t, db, nil, func(col arangodb.Collection) { + withContextT(t, defaultTestTimeout, func(ctx context.Context, tb testing.TB) { + doc1 := DocWithCode{ + Key: "test", + Code: "code1", + } + doc2 := DocWithCode{ + Key: "test2", + Code: "code2", + } + readerCrt, err := col.CreateDocuments(ctx, []any{doc1, doc2}) + require.NoError(t, err) + metaCrt, errs := readerCrt.ReadAll() + require.Equal(t, 2, len(metaCrt)) // Verify we got 2 results + require.ElementsMatch(t, []any{doc1.Key, doc2.Key}, []any{metaCrt[0].Key, metaCrt[1].Key}) + require.ElementsMatch(t, []any{nil, nil}, errs) + + var docRedRead []DocWithCode + + readeRed, err := col.ReadDocuments(ctx, []string{ + "test", "test2", "nonexistent", + }) + require.NoError(t, err) + + metaRed, errs := readeRed.ReadAll(&docRedRead) + + require.ElementsMatch(t, []any{doc1.Key, doc2.Key}, []any{metaRed[0].Key, metaRed[1].Key}) + require.ElementsMatch(t, []any{nil, nil, shared.ErrArangoDocumentNotFound}, []any{errs[0], errs[1], errs[2].(shared.ArangoError).ErrorNum}) + + var docOldObject DocWithCode + var docDelRead []DocWithCode + + readerDel, err := col.DeleteDocumentsWithOptions(ctx, []string{ + "test", "test2", "nonexistent", + }, &arangodb.CollectionDocumentDeleteOptions{OldObject: &docOldObject}) + require.NoError(t, err) + metaDel, errs := readerDel.ReadAll(&docDelRead) + + require.ElementsMatch(t, []any{doc1.Key, doc2.Key, ""}, []any{metaDel[0].Key, metaDel[1].Key, metaDel[2].Key}) + require.ElementsMatch(t, []any{nil, nil, shared.ErrArangoDocumentNotFound}, []any{errs[0], errs[1], errs[2].(shared.ArangoError).ErrorNum}) + + // Now this should work correctly with separate Old objects + require.ElementsMatch(t, []any{doc1.Code, doc2.Code}, []any{metaDel[0].Old.(*DocWithCode).Code, metaDel[1].Old.(*DocWithCode).Code}) + + }) + }) + }) }) } diff --git a/v2/tests/database_collection_doc_delete_test.go b/v2/tests/database_collection_doc_delete_test.go index 2d149f80..b171fb5f 100644 --- a/v2/tests/database_collection_doc_delete_test.go +++ b/v2/tests/database_collection_doc_delete_test.go @@ -151,7 +151,8 @@ func Test_DatabaseCollectionDocDeleteSimple(t *testing.T) { } require.NoError(t, err, meta) require.Equal(t, keys[i], meta.Key) - require.Equal(t, keys[i], oldDoc.Key) + require.NotNil(t, meta.Old) + require.Equal(t, keys[i], meta.Old.(*document).Key) } }) }) diff --git a/v2/tests/database_collection_operations_test.go b/v2/tests/database_collection_operations_test.go index 0291e2f2..b32d1a55 100644 --- a/v2/tests/database_collection_operations_test.go +++ b/v2/tests/database_collection_operations_test.go @@ -270,6 +270,7 @@ func Test_DatabaseCollectionOperations(t *testing.T) { require.NoError(t, err) r, err := col.ReadDocuments(ctx, docsIds) + require.NoError(t, err) nd := docs @@ -473,6 +474,127 @@ func Test_DatabaseCollectionOperations(t *testing.T) { require.Len(t, nd, 0) }) + + t.Run("Replace", func(t *testing.T) { + // Create some documents to replace + replaceDocs := newDocs(5) + for i := 0; i < 5; i++ { + replaceDocs[i].Fields = GenerateUUID("replace-test") + } + + // Create the documents first + _, err := col.CreateDocuments(ctx, replaceDocs) + require.NoError(t, err) + + // Now replace them + for i := 0; i < 5; i++ { + replaceDocs[i].Fields = GenerateUUID("replaced-test") + } + + var oldDoc document + var newDoc document + + _, err = col.ReplaceDocumentsWithOptions(ctx, replaceDocs, &arangodb.CollectionDocumentReplaceOptions{ + OldObject: &oldDoc, + NewObject: &newDoc, + }) + require.NoError(t, err) + }) + }) + }) + }) + }) +} + +func Test_DatabaseCollectionBulkOperations(t *testing.T) { + Wrap(t, func(t *testing.T, client arangodb.Client) { + WithDatabase(t, client, nil, func(db arangodb.Database) { + WithCollectionV2(t, db, nil, func(col arangodb.Collection) { + withContextT(t, defaultTestTimeout, func(ctx context.Context, tb testing.TB) { + size := 10 + docs := newDocs(size) + + for i := 0; i < size; i++ { + docs[i].Fields = GenerateUUID("test-doc-bulk") + } + + docsIds := docs.asBasic().getKeys() + + t.Run("Create_Bulk", func(t *testing.T) { + createReader, err := col.CreateDocuments(ctx, docs) + require.NoError(t, err) + + // Test bulk create operation + createResults, createErrs := createReader.ReadAll() + require.Equal(t, size, len(createResults)) + require.Equal(t, size, len(createErrs)) + for _, err := range createErrs { + require.NoError(t, err) + } + }) + + t.Run("Read_Bulk", func(t *testing.T) { + readReader, err := col.ReadDocuments(ctx, docsIds) + require.NoError(t, err) + + // Test bulk read operation + var readResults []document + readResponses, readErrs := readReader.ReadAll(&readResults) + require.Equal(t, size, len(readResponses)) + require.Equal(t, size, len(readErrs)) + require.Equal(t, size, len(readResults)) + for _, err := range readErrs { + require.NoError(t, err) + } + }) + + t.Run("Update_Bulk", func(t *testing.T) { + // Update the documents + for i := 0; i < size; i++ { + docs[i].Fields = GenerateUUID("updated-test-doc") + } + + var oldDoc document + var newDoc document + + updateReader, err := col.UpdateDocumentsWithOptions(ctx, docs, &arangodb.CollectionDocumentUpdateOptions{ + OldObject: &oldDoc, + NewObject: &newDoc, + }) + require.NoError(t, err) + + // Test bulk update operation + updateResults, updateErrs := updateReader.ReadAll() + require.Equal(t, size, len(updateResults)) + require.Equal(t, size, len(updateErrs)) + for _, err := range updateErrs { + require.NoError(t, err) + } + }) + + t.Run("Replace_Bulk", func(t *testing.T) { + // Replace the documents + for i := 0; i < size; i++ { + docs[i].Fields = GenerateUUID("replaced-test-doc") + } + + var oldDoc document + var newDoc document + + replaceReader, err := col.ReplaceDocumentsWithOptions(ctx, docs, &arangodb.CollectionDocumentReplaceOptions{ + OldObject: &oldDoc, + NewObject: &newDoc, + }) + require.NoError(t, err) + + // Test bulk replace operation + replaceResults, replaceErrs := replaceReader.ReadAll() + require.Equal(t, size, len(replaceResults)) + require.Equal(t, size, len(replaceErrs)) + for _, err := range replaceErrs { + require.NoError(t, err) + } + }) }) }) })