You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
677 lines
23 KiB
677 lines
23 KiB
//
|
|
// DISCLAIMER
|
|
//
|
|
// Copyright 2017 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
|
|
//
|
|
// Author Ewout Prangsma
|
|
//
|
|
|
|
package driver
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path"
|
|
"reflect"
|
|
)
|
|
|
|
// DocumentExists checks if a document with given key exists in the collection.
|
|
func (c *collection) DocumentExists(ctx context.Context, key string) (bool, error) {
|
|
if err := validateKey(key); err != nil {
|
|
return false, WithStack(err)
|
|
}
|
|
escapedKey := pathEscape(key)
|
|
req, err := c.conn.NewRequest("HEAD", path.Join(c.relPath("document"), escapedKey))
|
|
if err != nil {
|
|
return false, WithStack(err)
|
|
}
|
|
applyContextSettings(ctx, req)
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return false, WithStack(err)
|
|
}
|
|
found := resp.StatusCode() == 200
|
|
return found, nil
|
|
}
|
|
|
|
// ReadDocument reads a single document with given key from the collection.
|
|
// The document data is stored into result, the document meta data is returned.
|
|
// If no document exists with given key, a NotFoundError is returned.
|
|
func (c *collection) ReadDocument(ctx context.Context, key string, result interface{}) (DocumentMeta, error) {
|
|
if err := validateKey(key); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
escapedKey := pathEscape(key)
|
|
req, err := c.conn.NewRequest("GET", path.Join(c.relPath("document"), escapedKey))
|
|
if err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
// This line introduces a lot of side effects. In particular If-Match headers are now set (which is a bugfix)
|
|
// and invalid query parameters like waitForSync (which is potentially breaking change)
|
|
cs := applyContextSettings(ctx, req)
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
// Parse metadata
|
|
var meta DocumentMeta
|
|
if err := resp.ParseBody("", &meta); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
// load context response values
|
|
loadContextResponseValues(cs, resp)
|
|
// Parse result
|
|
if result != nil {
|
|
if err := resp.ParseBody("", result); err != nil {
|
|
return meta, WithStack(err)
|
|
}
|
|
}
|
|
return meta, nil
|
|
}
|
|
|
|
// ReadDocuments reads multiple documents with given keys from the collection.
|
|
// The documents data is stored into elements of the given results slice,
|
|
// the documents meta data is returned.
|
|
// If no document exists with a given key, a NotFoundError is returned at its errors index.
|
|
func (c *collection) ReadDocuments(ctx context.Context, keys []string, results interface{}) (DocumentMetaSlice, ErrorSlice, error) {
|
|
resultsVal := reflect.ValueOf(results)
|
|
switch resultsVal.Kind() {
|
|
case reflect.Array, reflect.Slice:
|
|
// OK
|
|
default:
|
|
return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("results data must be of kind Array, got %s", resultsVal.Kind())})
|
|
}
|
|
if keys == nil {
|
|
return nil, nil, WithStack(InvalidArgumentError{Message: "keys nil"})
|
|
}
|
|
resultCount := resultsVal.Len()
|
|
if len(keys) != resultCount {
|
|
return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("expected %d keys, got %d", resultCount, len(keys))})
|
|
}
|
|
for _, key := range keys {
|
|
if err := validateKey(key); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
}
|
|
req, err := c.conn.NewRequest("PUT", c.relPath("document"))
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
req = req.SetQuery("onlyget", "1")
|
|
cs := applyContextSettings(ctx, req)
|
|
if _, err := req.SetBodyArray(keys, nil); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
// load context response values
|
|
loadContextResponseValues(cs, resp)
|
|
// Parse response array
|
|
metas, errs, err := parseResponseArray(resp, resultCount, cs, results)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
return metas, errs, nil
|
|
|
|
}
|
|
|
|
// CreateDocument creates a single document in the collection.
|
|
// The document data is loaded from the given document, the document meta data is returned.
|
|
// If the document data already contains a `_key` field, this will be used as key of the new document,
|
|
// otherwise a unique key is created.
|
|
// A ConflictError is returned when a `_key` field contains a duplicate key, other any other field violates an index constraint.
|
|
// To return the NEW document, prepare a context with `WithReturnNew`.
|
|
// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
|
|
func (c *collection) CreateDocument(ctx context.Context, document interface{}) (DocumentMeta, error) {
|
|
if document == nil {
|
|
return DocumentMeta{}, WithStack(InvalidArgumentError{Message: "document nil"})
|
|
}
|
|
req, err := c.conn.NewRequest("POST", c.relPath("document"))
|
|
if err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if _, err := req.SetBody(document); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
cs := applyContextSettings(ctx, req)
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(201, 202); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if cs.Silent {
|
|
// Empty response, we're done
|
|
return DocumentMeta{}, nil
|
|
}
|
|
// Parse metadata
|
|
var meta DocumentMeta
|
|
if err := resp.ParseBody("", &meta); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
// Parse returnNew (if needed)
|
|
if cs.ReturnNew != nil {
|
|
if err := resp.ParseBody("new", cs.ReturnNew); err != nil {
|
|
return meta, WithStack(err)
|
|
}
|
|
}
|
|
return meta, nil
|
|
}
|
|
|
|
// CreateDocuments creates multiple documents in the collection.
|
|
// The document data is loaded from the given documents slice, the documents meta data is returned.
|
|
// If a documents element already contains a `_key` field, this will be used as key of the new document,
|
|
// otherwise a unique key is created.
|
|
// If a documents element contains a `_key` field with a duplicate key, other any other field violates an index constraint,
|
|
// a ConflictError is returned in its inded in the errors slice.
|
|
// To return the NEW documents, prepare a context with `WithReturnNew`. The data argument passed to `WithReturnNew` must be
|
|
// a slice with the same number of entries as the `documents` slice.
|
|
// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
|
|
// If the create request itself fails or one of the arguments is invalid, an error is returned.
|
|
func (c *collection) CreateDocuments(ctx context.Context, documents interface{}) (DocumentMetaSlice, ErrorSlice, error) {
|
|
documentsVal := reflect.ValueOf(documents)
|
|
switch documentsVal.Kind() {
|
|
case reflect.Array, reflect.Slice:
|
|
// OK
|
|
default:
|
|
return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("documents data must be of kind Array, got %s", documentsVal.Kind())})
|
|
}
|
|
documentCount := documentsVal.Len()
|
|
req, err := c.conn.NewRequest("POST", c.relPath("document"))
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if _, err := req.SetBody(documents); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
cs := applyContextSettings(ctx, req)
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(201, 202); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if cs.Silent {
|
|
// Empty response, we're done
|
|
return nil, nil, nil
|
|
}
|
|
// Parse response array
|
|
metas, errs, err := parseResponseArray(resp, documentCount, cs, nil)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
return metas, errs, nil
|
|
}
|
|
|
|
// UpdateDocument updates a single document with given key in the collection.
|
|
// The document meta data is returned.
|
|
// To return the NEW document, prepare a context with `WithReturnNew`.
|
|
// To return the OLD document, prepare a context with `WithReturnOld`.
|
|
// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
|
|
// If no document exists with given key, a NotFoundError is returned.
|
|
func (c *collection) UpdateDocument(ctx context.Context, key string, update interface{}) (DocumentMeta, error) {
|
|
if err := validateKey(key); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if update == nil {
|
|
return DocumentMeta{}, WithStack(InvalidArgumentError{Message: "update nil"})
|
|
}
|
|
escapedKey := pathEscape(key)
|
|
req, err := c.conn.NewRequest("PATCH", path.Join(c.relPath("document"), escapedKey))
|
|
if err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if _, err := req.SetBody(update); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
cs := applyContextSettings(ctx, req)
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(201, 202); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if cs.Silent {
|
|
// Empty response, we're done
|
|
return DocumentMeta{}, nil
|
|
}
|
|
// Parse metadata
|
|
var meta DocumentMeta
|
|
if err := resp.ParseBody("", &meta); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
// Parse returnOld (if needed)
|
|
if cs.ReturnOld != nil {
|
|
if err := resp.ParseBody("old", cs.ReturnOld); err != nil {
|
|
return meta, WithStack(err)
|
|
}
|
|
}
|
|
// Parse returnNew (if needed)
|
|
if cs.ReturnNew != nil {
|
|
if err := resp.ParseBody("new", cs.ReturnNew); err != nil {
|
|
return meta, WithStack(err)
|
|
}
|
|
}
|
|
return meta, nil
|
|
}
|
|
|
|
// UpdateDocuments updates multiple document with given keys in the collection.
|
|
// The updates are loaded from the given updates slice, the documents meta data are returned.
|
|
// To return the NEW documents, prepare a context with `WithReturnNew` with a slice of documents.
|
|
// To return the OLD documents, prepare a context with `WithReturnOld` with a slice of documents.
|
|
// To wait until documents has been synced to disk, prepare a context with `WithWaitForSync`.
|
|
// If no document exists with a given key, a NotFoundError is returned at its errors index.
|
|
func (c *collection) UpdateDocuments(ctx context.Context, keys []string, updates interface{}) (DocumentMetaSlice, ErrorSlice, error) {
|
|
updatesVal := reflect.ValueOf(updates)
|
|
switch updatesVal.Kind() {
|
|
case reflect.Array, reflect.Slice:
|
|
// OK
|
|
default:
|
|
return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("updates data must be of kind Array, got %s", updatesVal.Kind())})
|
|
}
|
|
updateCount := updatesVal.Len()
|
|
if keys != nil {
|
|
if len(keys) != updateCount {
|
|
return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("expected %d keys, got %d", updateCount, len(keys))})
|
|
}
|
|
for _, key := range keys {
|
|
if err := validateKey(key); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
}
|
|
}
|
|
req, err := c.conn.NewRequest("PATCH", c.relPath("document"))
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
cs := applyContextSettings(ctx, req)
|
|
mergeArray, err := createMergeArray(keys, cs.Revisions)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if _, err := req.SetBodyArray(updates, mergeArray); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(201, 202); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if cs.Silent {
|
|
// Empty response, we're done
|
|
return nil, nil, nil
|
|
}
|
|
// Parse response array
|
|
metas, errs, err := parseResponseArray(resp, updateCount, cs, nil)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
return metas, errs, nil
|
|
}
|
|
|
|
// ReplaceDocument replaces a single document with given key in the collection with the document given in the document argument.
|
|
// The document meta data is returned.
|
|
// To return the NEW document, prepare a context with `WithReturnNew`.
|
|
// To return the OLD document, prepare a context with `WithReturnOld`.
|
|
// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
|
|
// If no document exists with given key, a NotFoundError is returned.
|
|
func (c *collection) ReplaceDocument(ctx context.Context, key string, document interface{}) (DocumentMeta, error) {
|
|
if err := validateKey(key); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if document == nil {
|
|
return DocumentMeta{}, WithStack(InvalidArgumentError{Message: "document nil"})
|
|
}
|
|
escapedKey := pathEscape(key)
|
|
req, err := c.conn.NewRequest("PUT", path.Join(c.relPath("document"), escapedKey))
|
|
if err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if _, err := req.SetBody(document); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
cs := applyContextSettings(ctx, req)
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(201, 202); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if cs.Silent {
|
|
// Empty response, we're done
|
|
return DocumentMeta{}, nil
|
|
}
|
|
// Parse metadata
|
|
var meta DocumentMeta
|
|
if err := resp.ParseBody("", &meta); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
// Parse returnOld (if needed)
|
|
if cs.ReturnOld != nil {
|
|
if err := resp.ParseBody("old", cs.ReturnOld); err != nil {
|
|
return meta, WithStack(err)
|
|
}
|
|
}
|
|
// Parse returnNew (if needed)
|
|
if cs.ReturnNew != nil {
|
|
if err := resp.ParseBody("new", cs.ReturnNew); err != nil {
|
|
return meta, WithStack(err)
|
|
}
|
|
}
|
|
return meta, nil
|
|
}
|
|
|
|
// ReplaceDocuments replaces multiple documents with given keys in the collection with the documents given in the documents argument.
|
|
// The replacements are loaded from the given documents slice, the documents meta data are returned.
|
|
// To return the NEW documents, prepare a context with `WithReturnNew` with a slice of documents.
|
|
// To return the OLD documents, prepare a context with `WithReturnOld` with a slice of documents.
|
|
// To wait until documents has been synced to disk, prepare a context with `WithWaitForSync`.
|
|
// If no document exists with a given key, a NotFoundError is returned at its errors index.
|
|
func (c *collection) ReplaceDocuments(ctx context.Context, keys []string, documents interface{}) (DocumentMetaSlice, ErrorSlice, error) {
|
|
documentsVal := reflect.ValueOf(documents)
|
|
switch documentsVal.Kind() {
|
|
case reflect.Array, reflect.Slice:
|
|
// OK
|
|
default:
|
|
return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("documents data must be of kind Array, got %s", documentsVal.Kind())})
|
|
}
|
|
documentCount := documentsVal.Len()
|
|
if keys != nil {
|
|
if len(keys) != documentCount {
|
|
return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("expected %d keys, got %d", documentCount, len(keys))})
|
|
}
|
|
for _, key := range keys {
|
|
if err := validateKey(key); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
}
|
|
}
|
|
req, err := c.conn.NewRequest("PUT", c.relPath("document"))
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
cs := applyContextSettings(ctx, req)
|
|
mergeArray, err := createMergeArray(keys, cs.Revisions)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if _, err := req.SetBodyArray(documents, mergeArray); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(201, 202); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if cs.Silent {
|
|
// Empty response, we're done
|
|
return nil, nil, nil
|
|
}
|
|
// Parse response array
|
|
metas, errs, err := parseResponseArray(resp, documentCount, cs, nil)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
return metas, errs, nil
|
|
}
|
|
|
|
// RemoveDocument removes a single document with given key from the collection.
|
|
// The document meta data is returned.
|
|
// To return the OLD document, prepare a context with `WithReturnOld`.
|
|
// To wait until removal has been synced to disk, prepare a context with `WithWaitForSync`.
|
|
// If no document exists with given key, a NotFoundError is returned.
|
|
func (c *collection) RemoveDocument(ctx context.Context, key string) (DocumentMeta, error) {
|
|
if err := validateKey(key); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
escapedKey := pathEscape(key)
|
|
req, err := c.conn.NewRequest("DELETE", path.Join(c.relPath("document"), escapedKey))
|
|
if err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
cs := applyContextSettings(ctx, req)
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200, 202); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
if cs.Silent {
|
|
// Empty response, we're done
|
|
return DocumentMeta{}, nil
|
|
}
|
|
// Parse metadata
|
|
var meta DocumentMeta
|
|
if err := resp.ParseBody("", &meta); err != nil {
|
|
return DocumentMeta{}, WithStack(err)
|
|
}
|
|
// Parse returnOld (if needed)
|
|
if cs.ReturnOld != nil {
|
|
if err := resp.ParseBody("old", cs.ReturnOld); err != nil {
|
|
return meta, WithStack(err)
|
|
}
|
|
}
|
|
return meta, nil
|
|
}
|
|
|
|
// RemoveDocuments removes multiple documents with given keys from the collection.
|
|
// The document meta data are returned.
|
|
// To return the OLD documents, prepare a context with `WithReturnOld` with a slice of documents.
|
|
// To wait until removal has been synced to disk, prepare a context with `WithWaitForSync`.
|
|
// If no document exists with a given key, a NotFoundError is returned at its errors index.
|
|
func (c *collection) RemoveDocuments(ctx context.Context, keys []string) (DocumentMetaSlice, ErrorSlice, error) {
|
|
for _, key := range keys {
|
|
if err := validateKey(key); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
}
|
|
keyCount := len(keys)
|
|
req, err := c.conn.NewRequest("DELETE", c.relPath("document"))
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
cs := applyContextSettings(ctx, req)
|
|
metaArray, err := createMergeArray(keys, cs.Revisions)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if _, err := req.SetBodyArray(metaArray, nil); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200, 202); err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
if cs.Silent {
|
|
// Empty response, we're done
|
|
return nil, nil, nil
|
|
}
|
|
// Parse response array
|
|
metas, errs, err := parseResponseArray(resp, keyCount, cs, nil)
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
return metas, errs, nil
|
|
}
|
|
|
|
// ImportDocuments imports one or more documents into the collection.
|
|
// The document data is loaded from the given documents argument, statistics are returned.
|
|
// The documents argument can be one of the following:
|
|
// - An array of structs: All structs will be imported as individual documents.
|
|
// - An array of maps: All maps will be imported as individual documents.
|
|
// To wait until all documents have been synced to disk, prepare a context with `WithWaitForSync`.
|
|
// To return details about documents that could not be imported, prepare a context with `WithImportDetails`.
|
|
func (c *collection) ImportDocuments(ctx context.Context, documents interface{}, options *ImportDocumentOptions) (ImportDocumentStatistics, error) {
|
|
documentsVal := reflect.ValueOf(documents)
|
|
switch documentsVal.Kind() {
|
|
case reflect.Array, reflect.Slice:
|
|
// OK
|
|
default:
|
|
return ImportDocumentStatistics{}, WithStack(InvalidArgumentError{Message: fmt.Sprintf("documents data must be of kind Array, got %s", documentsVal.Kind())})
|
|
}
|
|
req, err := c.conn.NewRequest("POST", path.Join(c.db.relPath(), "_api/import"))
|
|
if err != nil {
|
|
return ImportDocumentStatistics{}, WithStack(err)
|
|
}
|
|
req.SetQuery("collection", c.name)
|
|
req.SetQuery("type", "documents")
|
|
if options != nil {
|
|
if v := options.FromPrefix; v != "" {
|
|
req.SetQuery("fromPrefix", v)
|
|
}
|
|
if v := options.ToPrefix; v != "" {
|
|
req.SetQuery("toPrefix", v)
|
|
}
|
|
if v := options.Overwrite; v {
|
|
req.SetQuery("overwrite", "true")
|
|
}
|
|
if v := options.OnDuplicate; v != "" {
|
|
req.SetQuery("onDuplicate", string(v))
|
|
}
|
|
if v := options.Complete; v {
|
|
req.SetQuery("complete", "true")
|
|
}
|
|
}
|
|
if _, err := req.SetBodyImportArray(documents); err != nil {
|
|
return ImportDocumentStatistics{}, WithStack(err)
|
|
}
|
|
cs := applyContextSettings(ctx, req)
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return ImportDocumentStatistics{}, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(201); err != nil {
|
|
return ImportDocumentStatistics{}, WithStack(err)
|
|
}
|
|
// Parse response
|
|
var data ImportDocumentStatistics
|
|
if err := resp.ParseBody("", &data); err != nil {
|
|
return ImportDocumentStatistics{}, WithStack(err)
|
|
}
|
|
// Import details (if needed)
|
|
if details := cs.ImportDetails; details != nil {
|
|
if err := resp.ParseBody("details", details); err != nil {
|
|
return ImportDocumentStatistics{}, WithStack(err)
|
|
}
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// createMergeArray returns an array of metadata maps with `_key` and/or `_rev` elements.
|
|
func createMergeArray(keys, revs []string) ([]map[string]interface{}, error) {
|
|
if keys == nil && revs == nil {
|
|
return nil, nil
|
|
}
|
|
if revs == nil {
|
|
mergeArray := make([]map[string]interface{}, len(keys))
|
|
for i, k := range keys {
|
|
mergeArray[i] = map[string]interface{}{
|
|
"_key": k,
|
|
}
|
|
}
|
|
return mergeArray, nil
|
|
}
|
|
if keys == nil {
|
|
mergeArray := make([]map[string]interface{}, len(revs))
|
|
for i, r := range revs {
|
|
mergeArray[i] = map[string]interface{}{
|
|
"_rev": r,
|
|
}
|
|
}
|
|
return mergeArray, nil
|
|
}
|
|
if len(keys) != len(revs) {
|
|
return nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("#keys must be equal to #revs, got %d, %d", len(keys), len(revs))})
|
|
}
|
|
mergeArray := make([]map[string]interface{}, len(keys))
|
|
for i, k := range keys {
|
|
mergeArray[i] = map[string]interface{}{
|
|
"_key": k,
|
|
"_rev": revs[i],
|
|
}
|
|
}
|
|
return mergeArray, nil
|
|
|
|
}
|
|
|
|
// parseResponseArray parses an array response in the given response
|
|
func parseResponseArray(resp Response, count int, cs contextSettings, results interface{}) (DocumentMetaSlice, ErrorSlice, error) {
|
|
resps, err := resp.ParseArrayBody()
|
|
if err != nil {
|
|
return nil, nil, WithStack(err)
|
|
}
|
|
metas := make(DocumentMetaSlice, count)
|
|
errs := make(ErrorSlice, count)
|
|
returnOldVal := reflect.ValueOf(cs.ReturnOld)
|
|
returnNewVal := reflect.ValueOf(cs.ReturnNew)
|
|
resultsVal := reflect.ValueOf(results)
|
|
for i := 0; i < count; i++ {
|
|
resp := resps[i]
|
|
var meta DocumentMeta
|
|
if err := resp.CheckStatus(200, 201, 202); err != nil {
|
|
errs[i] = err
|
|
} else {
|
|
if err := resp.ParseBody("", &meta); err != nil {
|
|
errs[i] = err
|
|
} else {
|
|
metas[i] = meta
|
|
// Parse returnOld (if needed)
|
|
if cs.ReturnOld != nil {
|
|
returnOldEntryVal := returnOldVal.Index(i).Addr()
|
|
if err := resp.ParseBody("old", returnOldEntryVal.Interface()); err != nil {
|
|
errs[i] = err
|
|
}
|
|
}
|
|
// Parse returnNew (if needed)
|
|
if cs.ReturnNew != nil {
|
|
returnNewEntryVal := returnNewVal.Index(i).Addr()
|
|
if err := resp.ParseBody("new", returnNewEntryVal.Interface()); err != nil {
|
|
errs[i] = err
|
|
}
|
|
}
|
|
}
|
|
if results != nil {
|
|
// Parse compare result document
|
|
resultsEntryVal := resultsVal.Index(i).Addr()
|
|
if err := resp.ParseBody("", resultsEntryVal.Interface()); err != nil {
|
|
errs[i] = err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return metas, errs, nil
|
|
}
|
|
|