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.
387 lines
11 KiB
387 lines
11 KiB
//
|
|
// DISCLAIMER
|
|
//
|
|
// Copyright 2017-2021 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
|
|
// Author Tomasz Mielech
|
|
//
|
|
|
|
package driver
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"path"
|
|
)
|
|
|
|
// newCollection creates a new Collection implementation.
|
|
func newCollection(name string, db *database) (Collection, error) {
|
|
if name == "" {
|
|
return nil, WithStack(InvalidArgumentError{Message: "name is empty"})
|
|
}
|
|
if db == nil {
|
|
return nil, WithStack(InvalidArgumentError{Message: "db is nil"})
|
|
}
|
|
return &collection{
|
|
name: name,
|
|
db: db,
|
|
conn: db.conn,
|
|
}, nil
|
|
}
|
|
|
|
type collection struct {
|
|
name string
|
|
db *database
|
|
conn Connection
|
|
}
|
|
|
|
// relPath creates the relative path to this collection (`_db/<db-name>/_api/<api-name>/<col-name>`)
|
|
func (c *collection) relPath(apiName string) string {
|
|
escapedName := pathEscape(c.name)
|
|
return path.Join(c.db.relPath(), "_api", apiName, escapedName)
|
|
}
|
|
|
|
// Name returns the name of the collection.
|
|
func (c *collection) Name() string {
|
|
return c.name
|
|
}
|
|
|
|
// Database returns the database containing the collection.
|
|
func (c *collection) Database() Database {
|
|
return c.db
|
|
}
|
|
|
|
// Status fetches the current status of the collection.
|
|
func (c *collection) Status(ctx context.Context) (CollectionStatus, error) {
|
|
req, err := c.conn.NewRequest("GET", c.relPath("collection"))
|
|
if err != nil {
|
|
return CollectionStatus(0), WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return CollectionStatus(0), WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return CollectionStatus(0), WithStack(err)
|
|
}
|
|
var data CollectionInfo
|
|
if err := resp.ParseBody("", &data); err != nil {
|
|
return CollectionStatus(0), WithStack(err)
|
|
}
|
|
return data.Status, nil
|
|
}
|
|
|
|
// Count fetches the number of document in the collection.
|
|
func (c *collection) Count(ctx context.Context) (int64, error) {
|
|
req, err := c.conn.NewRequest("GET", path.Join(c.relPath("collection"), "count"))
|
|
if err != nil {
|
|
return 0, WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return 0, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return 0, WithStack(err)
|
|
}
|
|
var data struct {
|
|
Count int64 `json:"count,omitempty"`
|
|
}
|
|
if err := resp.ParseBody("", &data); err != nil {
|
|
return 0, WithStack(err)
|
|
}
|
|
return data.Count, nil
|
|
}
|
|
|
|
// Statistics returns the number of documents and additional statistical information about the collection.
|
|
func (c *collection) Statistics(ctx context.Context) (CollectionStatistics, error) {
|
|
req, err := c.conn.NewRequest("GET", path.Join(c.relPath("collection"), "figures"))
|
|
if err != nil {
|
|
return CollectionStatistics{}, WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return CollectionStatistics{}, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return CollectionStatistics{}, WithStack(err)
|
|
}
|
|
var data CollectionStatistics
|
|
if err := resp.ParseBody("", &data); err != nil {
|
|
return CollectionStatistics{}, WithStack(err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// Revision fetches the revision ID of the collection.
|
|
// The revision ID is a server-generated string that clients can use to check whether data
|
|
// in a collection has changed since the last revision check.
|
|
func (c *collection) Revision(ctx context.Context) (string, error) {
|
|
req, err := c.conn.NewRequest("GET", path.Join(c.relPath("collection"), "revision"))
|
|
if err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
var data CollectionProperties
|
|
if err := resp.ParseBody("", &data); err != nil {
|
|
return "", WithStack(err)
|
|
}
|
|
return data.Revision, nil
|
|
}
|
|
|
|
// Checksum returns a checksum for the specified collection
|
|
// withRevisions - Whether to include document revision ids in the checksum calculation.
|
|
// withData - Whether to include document body data in the checksum calculation.
|
|
func (c *collection) Checksum(ctx context.Context, withRevisions bool, withData bool) (CollectionChecksum, error) {
|
|
var data CollectionChecksum
|
|
|
|
req, err := c.conn.NewRequest("GET", path.Join(c.relPath("collection"), "checksum"))
|
|
if err != nil {
|
|
return data, WithStack(err)
|
|
}
|
|
if withRevisions {
|
|
req.SetQuery("withRevisions", "true")
|
|
}
|
|
if withData {
|
|
req.SetQuery("withData", "true")
|
|
}
|
|
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return data, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return data, WithStack(err)
|
|
}
|
|
|
|
if err := resp.ParseBody("", &data); err != nil {
|
|
return data, WithStack(err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// Properties fetches extended information about the collection.
|
|
func (c *collection) Properties(ctx context.Context) (CollectionProperties, error) {
|
|
req, err := c.conn.NewRequest("GET", path.Join(c.relPath("collection"), "properties"))
|
|
if err != nil {
|
|
return CollectionProperties{}, WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return CollectionProperties{}, WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return CollectionProperties{}, WithStack(err)
|
|
}
|
|
|
|
var data struct {
|
|
CollectionProperties
|
|
ReplicationFactorV2 replicationFactor `json:"replicationFactor,omitempty"`
|
|
}
|
|
|
|
if err := resp.ParseBody("", &data); err != nil {
|
|
return CollectionProperties{}, WithStack(err)
|
|
}
|
|
data.ReplicationFactor = int(data.ReplicationFactorV2)
|
|
|
|
return data.CollectionProperties, nil
|
|
}
|
|
|
|
// SetProperties changes properties of the collection.
|
|
func (c *collection) SetProperties(ctx context.Context, options SetCollectionPropertiesOptions) error {
|
|
req, err := c.conn.NewRequest("PUT", path.Join(c.relPath("collection"), "properties"))
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if _, err := req.SetBody(options.asInternal()); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Shards fetches shards information of the collection.
|
|
func (c *collection) Shards(ctx context.Context, details bool) (CollectionShards, error) {
|
|
req, err := c.conn.NewRequest("GET", path.Join(c.relPath("collection"), "shards"))
|
|
if err != nil {
|
|
return CollectionShards{}, WithStack(err)
|
|
}
|
|
if details {
|
|
req.SetQuery("details", "true")
|
|
}
|
|
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return CollectionShards{}, WithStack(err)
|
|
}
|
|
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return CollectionShards{}, WithStack(err)
|
|
}
|
|
|
|
var data struct {
|
|
CollectionShards
|
|
ReplicationFactorV2 replicationFactor `json:"replicationFactor,omitempty"`
|
|
}
|
|
|
|
if err := resp.ParseBody("", &data); err != nil {
|
|
return CollectionShards{}, WithStack(err)
|
|
}
|
|
data.ReplicationFactor = int(data.ReplicationFactorV2)
|
|
|
|
return data.CollectionShards, nil
|
|
}
|
|
|
|
// Load the collection into memory.
|
|
func (c *collection) Load(ctx context.Context) error {
|
|
req, err := c.conn.NewRequest("PUT", path.Join(c.relPath("collection"), "load"))
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
opts := struct {
|
|
Count bool `json:"count"`
|
|
}{
|
|
Count: false,
|
|
}
|
|
if _, err := req.SetBody(opts); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UnLoad the collection from memory.
|
|
func (c *collection) Unload(ctx context.Context) error {
|
|
req, err := c.conn.NewRequest("PUT", path.Join(c.relPath("collection"), "unload"))
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
|
|
}
|
|
|
|
// Remove removes the entire collection.
|
|
// If the collection does not exist, a NotFoundError is returned.
|
|
func (c *collection) Remove(ctx context.Context) error {
|
|
req, err := c.conn.NewRequest("DELETE", c.relPath("collection"))
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
applyContextSettings(ctx, req)
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Truncate removes all documents from the collection, but leaves the indexes intact.
|
|
func (c *collection) Truncate(ctx context.Context) error {
|
|
req, err := c.conn.NewRequest("PUT", path.Join(c.relPath("collection"), "truncate"))
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
resp, err := c.conn.Do(ctx, req)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := resp.CheckStatus(200); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type setCollectionPropertiesOptionsInternal struct {
|
|
WaitForSync *bool `json:"waitForSync,omitempty"`
|
|
JournalSize int64 `json:"journalSize,omitempty"`
|
|
ReplicationFactor replicationFactor `json:"replicationFactor,omitempty"`
|
|
CacheEnabled *bool `json:"cacheEnabled,omitempty"`
|
|
ComputedValues []ComputedValue `json:"computedValues,omitempty"`
|
|
// Deprecated: use 'WriteConcern' instead
|
|
MinReplicationFactor int `json:"minReplicationFactor,omitempty"`
|
|
// Available from 3.6 arangod version.
|
|
WriteConcern int `json:"writeConcern,omitempty"`
|
|
Schema *CollectionSchemaOptions `json:"schema,omitempty"`
|
|
}
|
|
|
|
func (p *SetCollectionPropertiesOptions) asInternal() setCollectionPropertiesOptionsInternal {
|
|
return setCollectionPropertiesOptionsInternal{
|
|
WaitForSync: p.WaitForSync,
|
|
JournalSize: p.JournalSize,
|
|
CacheEnabled: p.CacheEnabled,
|
|
ComputedValues: p.ComputedValues,
|
|
ReplicationFactor: replicationFactor(p.ReplicationFactor),
|
|
MinReplicationFactor: p.MinReplicationFactor,
|
|
WriteConcern: p.WriteConcern,
|
|
Schema: p.Schema,
|
|
}
|
|
}
|
|
|
|
func (p *SetCollectionPropertiesOptions) fromInternal(i *setCollectionPropertiesOptionsInternal) {
|
|
p.WaitForSync = i.WaitForSync
|
|
p.JournalSize = i.JournalSize
|
|
p.CacheEnabled = i.CacheEnabled
|
|
p.ReplicationFactor = int(i.ReplicationFactor)
|
|
p.MinReplicationFactor = i.MinReplicationFactor
|
|
p.WriteConcern = i.WriteConcern
|
|
p.Schema = i.Schema
|
|
}
|
|
|
|
// MarshalJSON converts SetCollectionPropertiesOptions into json
|
|
func (p *SetCollectionPropertiesOptions) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(p.asInternal())
|
|
}
|
|
|
|
// UnmarshalJSON loads SetCollectionPropertiesOptions from json
|
|
func (p *SetCollectionPropertiesOptions) UnmarshalJSON(d []byte) error {
|
|
var internal setCollectionPropertiesOptionsInternal
|
|
if err := json.Unmarshal(d, &internal); err != nil {
|
|
return err
|
|
}
|
|
|
|
p.fromInternal(&internal)
|
|
return nil
|
|
}
|
|
|