<commit_before>---
title: Moray: Joyent's Key/Value Store.
markdown2extras: tables, code-friendly
apisections: Buckets, Objects
---

# Moray

This is the reference documentation for Moray, which is a key-value store built
on top of [Manatee](https://github.com/joyent/manatee/) and
[fast](https://github.com/joyent/node-fast).  For general information about
Moray, see the [README](https://github.com/joyent/moray).

This documentation provides descriptions of the APIs that Moray offers, as well
as how to use the (node.js) [client SDK](https://mo.joyent.com/node-moray) to
interact with Moray.

## Conventions

Any content formatted like this:

    $ getbucket foo

is a command-line example that you can run from a shell. All other examples and
information are formatted like this:

    client.createBucket(foo, {}, function (err) {
        assert.ifError(err);
    });


# Overview

Moray allows you to store arbitrary JSON documents (called **objects**) inside
namespaces called **buckets**.  Buckets can have **indexes**, which are
top-level fields that are extracted so that search operations using those fields
can be executed efficiently.  Indexes can also be used to enforce uniqueness of
a value.  Since buckets are essentially namespaces, bucket `foo` can have a key
`mykey` and bucket `bar` can have a key `mykey` with different values.

There is no limit on the number of buckets, nor on the number of keys in a
bucket.  That said, a single Moray instance is backed by a single logical
Postgres instance, so you are practically limited to how much data you can
maintain.

The basic operations on objects in a bucket are `put`, `get`, and `delete`.
`search`, `update`, and `deleteMany` are also supported using filter strings
that operate on indexed fields.

You define a bucket's indexes when you initially create the bucket.  When you
write an object, the value of each indexed field is updated in the server-side
index for that field.  You can update a bucket's indexes later, but objects
already written in that bucket will need to be reindexed.

Indexes can be defined to be of type `number`, `boolean`, `string`, `ip` or
`subnet`.  Index names must:

- Contain only Latin letters, Arabic numerals, and underscores.
- Start with a Latin letter or an underscore.
- Not end with an underscore.
- When starting with an underscore, not contain any further underscores.
- Not start with `moray`.
- Not be a reserved name (`_etag`, `_id`, `_key`, `_atime`, `_ctime`, `_mtime`,
  `_rver`, `_txn_snap`, `_value`, or `_vnode`).

Moray also supports multi-valued entries such that indexing still works (mostly)
as expected.  There's an example later in this document under "Using Arrays".


# Basic MorayClient usage

You can install the node-moray client library and CLI tools using:

    $ npm install moray

Or, to install the CLI tools and manual pages on your path:

    $ npm install -g moray

The CLI tools have detailed manual pages.  Start with `moray(1)` for an
overview.

The library interface has an overview manual page `moray(3)` that describes how
to initialize the client for Triton and Manta services and CLI tools.  The API
documentation below includes examples of using both the CLI tools and Node
client library interfaces.  It's worth reviewing `moray(1)` and `moray(3)` to
understand the basic conventions used below.


# Common Options

There are some options that can be used with several RPCs. They are documented
here, and linked to from each of the RPCs where they are valid to use.

##### <a name="option-etag">etag</a>

An `etag` that an already existing object must first have before being affected.
If the `etag` on the existing object differs from the one specified in the
options, then the server will return an `EtagConflictError`. See
[PutObject](#putobject) for further details.

##### <a name="option-limit">limit</a>

A number that limits the number of records that are processed by the request.
Default is `1000`, unless `noLimit` is set to `true`.

Using large values (> 1000, or setting `noLimit` to `true`) is considered
dangerous. The design center of Moray is around short-lived requests, and there
are serious consequences in PostgreSQL for leaving connections open in
transactions for extended periods while other transactions are making changes.

##### <a name="option-nobucketcache">noBucketCache</a>

A boolean which, when set to `true`, makes Moray refresh its bucket cache before
handling the request. Default is `false`.

The bucket cache should generally not be be disabled (i.e `noBucketCache` set to
`true`). It is key to ensuring that the performance of a Moray server scales
with the number of requests. This feature primarily exists to write internal
Moray tests.

##### <a name="option-no_count">no_count</a>

A boolean which, when set to `true`, makes Moray not include a `_count` property
on each record sent with the response. Default is `false`.

The purpose of this option is to avoid performing a potentially expensive
`COUNT` query in addition to the query that fetches data records.

##### <a name="option-nolimit">noLimit</a>

A boolean which, when set to `true` and `limit` is not set, makes Moray not have
a maximum number of records processed by the request. Default is `false`.

Using this option is __considered dangerous__. The design center of Moray is
around short-lived requests, and there are serious consequences in PostgreSQL
for leaving connections open in transactions for extended periods while other
transactions are making changes.

##### <a name="option-offset">offset</a>

A number which indicates the offset at which Moray starts processing records
that would be returned by the same request with no `offset`. Default is `0`.

##### <a name="option-requireonlinereindexing">requireOnlineReindexing</a>

When passing `requireOnlineReindexing: true`, the client will make sure that
the server supports safely reading the contents of a reindexing bucket. Recent
versions of the server will always behave correctly here. This option is to
allow clients to make certain that they receive an `UnhandledOptionsError`
instead of having their data corrupted if they suspect that they are talking
to an older server.

##### <a name="option-req_id">req_id</a>

A string that can be used to track a request. Default is an automatically
generated V4 UUID.

##### <a name="option-sort">sort</a>

An object or an array of objects with the following properties:

* `attribute`: a required property of type string representing a field name on
  which to sort the result

* `order`: an optional property of type string that is either `ASC` or `DESC` to
  sort with ascending or descending order respectively. Default value is `ASC`.

Default value is `undefined`.

##### <a name="option-timeout">timeout</a>

A number that represents the delay in milliseconds that the request waits on a
reply from its underlying Postgres query before it errors with a
`QueryTimeoutError`. Default is `30000` (30 seconds).

Using this option is __strongly discouraged__. For identifying network failures,
TCP keep-alive, which is what the node-moray client library uses, is a better
fit. Otherwise, if the request hasn't completed, then the database is stuck
processing that request, and applications cannot generally do anything safely
except to wait (or possibly raise an alarm). Moreover, expiration of this
timeout does not cancel the underlying PostgreSQL query and the corresponding
PostgreSQL resources remain in use until the query ultimately does complete.


# Buckets

## CreateBucket

Creates a new bucket given a name and a `config` object.  The `config` object
defines an `index` section and optionally a `pre`, `post` array of trigger
functions.  Also, note that buckets can be versioned, such that subsequent
updates will fail if the version rolls backwards.

### API

A "fully loaded" config (without post triggers) would look like this:

    var cfg = {
        index: {
            email: {
                type: "string",
                unique: true
            },
            userid: {
                type: "number",
                unique: true
            },
            ismanager: {
                type: "boolean"
            }
        },
        pre: [
            function enforceEmail(req, cb) {
                if (!req.value.email)
                    return (cb(new Error('email is required')));

                return (cb());
            },
            function setUID(req, cb) {
                req.value.userid = Math.floor(Math.random() * 100001);
                cb();
            }
        ],
        options: {
            version: 1
        }
    }

    client.createBucket('foo', cfg, function (err) {
        assert.ifError(err);
    });

### Inputs

| Field    | Type     | Description                                     |
| -------- | -------- | ----------------------------------------------- |
| name     | string   | globally unique name for the bucket             |
| config   | object   | configuration (indexes/pre/post) for the bucket |
| options  | object   | any optional parameters, see below              |
| callback | function | only argument is `err`                          |

#### Options

- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `InvalidBucketConfigError`
* `InvalidBucketNameError`
* `InvalidIndexDefinitionError`
* `NoDatabasePeersError`
* `NotFunctionError`

Plus any currently unhandled Postgres Errors (such as relation already exists).

### CLI

To add that config above, you can use this invocation (`-s says wait for stdin`):

    $ putbucket -s foo < test.json

Or if you're not using triggers (more common, actually):

    $ putbucket -u email -u userid:number -i ismanager:boolean foo

Note there is no `createbucket` command; `putbucket` wraps up `CreateBucket` and
`UpdateBucket`.

## GetBucket

Returns the configuration for the bucket only.  There really are no options here
besides the name of the bucket you want to fetch (and of course request\_id).

### API

     client.getBucket('foo', function (err, bucket) {
        assert.ifError(err);
        console.log(util.inspect(bucket));
    });

### Inputs

| Field    | Type     | Description                         |
| -------- | -------- | ----------------------------------- |
| name     | string   | globally unique name for the bucket |
| options  | object   | any optional parameters, see below  |
| callback | function | only argument is `err`              |

#### Options

- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `BucketNotFoundError`
* `NoDatabasePeersError`

### CLI

    $ getbucket foo

    {
      "name": foo",
      "index": {
        "email": {
          "type": "string",
          "unique": true
        },
        "userid": {
          "type": "number",
          "unique": true
        },
        "ismanager": {
          "type": "boolean"
        }
      },
      "pre": [
        "function enforceEmail(req, cb) {
           if (!req.value.email) {
             return (cb(new Error('email is required')));
           }
           return (cb());
         }",
        "function setUID(req, cb) {
          req.value.userid = Math.floor(Math.random() * 100001);
          cb();
        }"
      ],
      "post": [],
      "mtime": "2012-07-31T19:43:32.268Z"
    }

Note the `\n's` inside the `pre` functions were inserted for readability in this
document; they were not actually returned, and are not legal JSON.

## ListBuckets

Returns the configuration for all buckets.

### API

     client.listBuckets(function (err, buckets) {
        assert.ifError(err);
        console.log(util.inspect(buckets));
     });

### Inputs

| Field    | Type     | Description                        |
| -------- | -------- | ---------------------------------- |
| options  | object   | any optional parameters, see below |
| callback | function | only argument is `err`             |

#### Options

- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `NoDatabasePeersError`

### CLI

    $ listbuckets
    [{
      "name": foo",
      "index": {
        "email": {
          "type": "string",
          "unique": true
        },
        "userid": {
          "type": "number",
          "unique": true
        },
        "ismanager": {
          "type": "boolean"
        }
      },
      "pre": [
        "function enforceEmail(req, cb) {
           if (!req.value.email) {
             return (cb(new Error('email is required')));
           }
           return (cb());
         }",
        "function setUID(req, cb) {
          req.value.userid = Math.floor(Math.random() * 100001);
          cb();
        }"
      ],
      "post": [],
      "mtime": "2012-07-31T19:43:32.268Z"
    }]

Note the `\n's` inside the `pre` functions were inserted for readability in this
document; they were not actually returned, and are not legal JSON.

## UpdateBucket

Updates a bucket given a name and a `config` object.  Basically, looks exactly
like `CreateBucket`.  The only reason we have `create|update bucket` and not
`putBucket` is so that users can selectively _not_ stomp all over a bucket if
they don't intend to (note there is a `putBucket` API in the client).

The `version` param in options is required if you are using versioned buckets,
and an error will be thrown if the version you are sending is `<=` than the
version in the database.

Note that if you *add* new indexes via `updateBucket`, then any *new* data will
be indexed accordingly, but _old_ data will not.  See the ReindexObjects
function for a method to update old rows. (A bucket version _must_ be specified
for `reindexObjects` to work.)

Also, note that all operations involving the bucket configuration in Moray will
use a cached copy of the bucket configuration, so it may take a few minutes for
your update to take effect.  This caching can be overridden during subsequent
operations by setting the `noBucketCache` option to true.

### API

    var cfg = {
        index: {
            email: {
                type: "string",
                unique: true
            },
            userid: {
                type: "number",
                unique: true
            }
        },
        pre: [],
        post: []
    }

    client.updateBucket('foo', cfg, function (err) {
        assert.ifError(err);
    });

Or alternatively:

    client.putBucket('foo', cfg, function (err) {
        assert.ifError(err);
    });

### Inputs

| Field    | Type     | Description                                     |
| -------- | -------- | ----------------------------------------------- |
| name     | string   | globally unique name for the bucket             |
| config   | object   | configuration (indexes/pre/post) for the bucket |
| options  | object   | any optional parameters, see below              |
| callback | function | only argument is `err`                          |

#### Options

- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `BucketNotFoundError`
* `InvalidBucketConfigError`
* `InvalidIndexDefinitionError`
* `NoDatabasePeersError`
* `NotFunctionError`

### CLI

    $ putbucket -s foo < test.json
    $ putbucket -u email -u userid:number foo

## DeleteBucket

Deletes a bucket, *and all data in that bucket!*

### API

    client.delBucket('foo', function (err) {
        assert.ifError(err);
    });

### Inputs

| Field    | Type     | Description                         |
| -------- | -------- | ----------------------------------- |
| name     | string   | globally unique name for the bucket |
| options  | object   | any optional parameters, see below  |
| callback | function | only argument is `err`              |

#### Options

- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `BucketNotFoundError`
* `NoDatabasePeersError`

### CLI

    $ delbucket foo

# Objects

With the exception of `ReindexObjects`, the object-related methods in Moray use
a cache for bucket information.  When a bucket is updated, say to alter its
index columns, stale data will persist in the bucket cache for up to 300
seconds.  For object operations that absolutely require current bucket
information, setting the `noBucketCache` option will bypass that cache.

## PutObject

Creates or overwrites an object given a bucket, key and value.  The bucket must
obviously already exist, and the key can be any string at all.  The value must
be a JS object of any form that can be run through `JSON.stringify()`.  Any keys
in the object that match the names of those defined in the bucket's `index`
section are indexed, and available for searching later.

As far as options, besides the usual `req_id`, notably you can set the flag
`etag`, which will make moray _atomically_ test/set your new value (this is
useful if you're implementing something like a lock semantic).  To ensure that
you're "creating" a record that never existed, set etag to `null`, otherwise set
it to the value of the last known version of `etag` you have.  If the etag differs
you'll receive an `EtagConflictError` which, in addition to your being able to
switch on the name, is decorated with the info you need; a sample `.toString()`
looks like:

    {
        name: 'EtagConflictError',
        message: '...',
        context: {
            bucket: 'moray_unit_test_2423db7',
            key: '<KEY>',
            expected: 'ABCDEFGH',
            actual: 'B21548FF'
        }
    }

### API

    var data = {
        email: '<EMAIL>',
        ismanager: false
    };
    client.putObject('foo', 'mcavage', data, function (err) {
        assert.ifError(err);
    });

### Inputs

| Field    | Type     | Description                             |
| -------- | -------- | --------------------------------------- |
| bucket   | string   | bucket to write this key in             |
| key      | string   | bucket to write this key in             |
| value    | object   | free-form JS object                     |
| options  | object   | any optional parameters, see below      |
| callback | function | only argument is `err`                  |

#### Options

- [etag](#option-etag)
- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `BucketNotFoundError`
* `EtagConflictError`
* `NoDatabasePeersError`
* `UniqueAttributeError`
* `InvalidIndexTypeError`

Plus any currently unhandled Postgres Errors (such as relation already exists).<commit_msg>MORAY-467 Document that PutObject returns the new etag Reviewed by: <NAME> <<EMAIL>> Approved by: <NAME> <<EMAIL>><commit_after>---
title: Moray: Joyent's Key/Value Store.
markdown2extras: tables, code-friendly
apisections: Buckets, Objects
---

# Moray

This is the reference documentation for Moray, which is a key-value store built
on top of [Manatee](https://github.com/joyent/manatee/) and
[fast](https://github.com/joyent/node-fast).  For general information about
Moray, see the [README](https://github.com/joyent/moray).

This documentation provides descriptions of the APIs that Moray offers, as well
as how to use the (node.js) [client SDK](https://mo.joyent.com/node-moray) to
interact with Moray.

## Conventions

Any content formatted like this:

    $ getbucket foo

is a command-line example that you can run from a shell. All other examples and
information are formatted like this:

    client.createBucket(foo, {}, function (err) {
        assert.ifError(err);
    });


# Overview

Moray allows you to store arbitrary JSON documents (called **objects**) inside
namespaces called **buckets**.  Buckets can have **indexes**, which are
top-level fields that are extracted so that search operations using those fields
can be executed efficiently.  Indexes can also be used to enforce uniqueness of
a value.  Since buckets are essentially namespaces, bucket `foo` can have a key
`mykey` and bucket `bar` can have a key `mykey` with different values.

There is no limit on the number of buckets, nor on the number of keys in a
bucket.  That said, a single Moray instance is backed by a single logical
Postgres instance, so you are practically limited to how much data you can
maintain.

The basic operations on objects in a bucket are `put`, `get`, and `delete`.
`search`, `update`, and `deleteMany` are also supported using filter strings
that operate on indexed fields.

You define a bucket's indexes when you initially create the bucket.  When you
write an object, the value of each indexed field is updated in the server-side
index for that field.  You can update a bucket's indexes later, but objects
already written in that bucket will need to be reindexed.

Indexes can be defined to be of type `number`, `boolean`, `string`, `ip` or
`subnet`.  Index names must:

- Contain only Latin letters, Arabic numerals, and underscores.
- Start with a Latin letter or an underscore.
- Not end with an underscore.
- When starting with an underscore, not contain any further underscores.
- Not start with `moray`.
- Not be a reserved name (`_etag`, `_id`, `_key`, `_atime`, `_ctime`, `_mtime`,
  `_rver`, `_txn_snap`, `_value`, or `_vnode`).

Moray also supports multi-valued entries such that indexing still works (mostly)
as expected.  There's an example later in this document under "Using Arrays".


# Basic MorayClient usage

You can install the node-moray client library and CLI tools using:

    $ npm install moray

Or, to install the CLI tools and manual pages on your path:

    $ npm install -g moray

The CLI tools have detailed manual pages.  Start with `moray(1)` for an
overview.

The library interface has an overview manual page `moray(3)` that describes how
to initialize the client for Triton and Manta services and CLI tools.  The API
documentation below includes examples of using both the CLI tools and Node
client library interfaces.  It's worth reviewing `moray(1)` and `moray(3)` to
understand the basic conventions used below.


# Common Options

There are some options that can be used with several RPCs. They are documented
here, and linked to from each of the RPCs where they are valid to use.

##### <a name="option-etag">etag</a>

An `etag` that an already existing object must first have before being affected.
If the `etag` on the existing object differs from the one specified in the
options, then the server will return an `EtagConflictError`. See
[PutObject](#putobject) for further details.

##### <a name="option-limit">limit</a>

A number that limits the number of records that are processed by the request.
Default is `1000`, unless `noLimit` is set to `true`.

Using large values (> 1000, or setting `noLimit` to `true`) is considered
dangerous. The design center of Moray is around short-lived requests, and there
are serious consequences in PostgreSQL for leaving connections open in
transactions for extended periods while other transactions are making changes.

##### <a name="option-nobucketcache">noBucketCache</a>

A boolean which, when set to `true`, makes Moray refresh its bucket cache before
handling the request. Default is `false`.

The bucket cache should generally not be be disabled (i.e `noBucketCache` set to
`true`). It is key to ensuring that the performance of a Moray server scales
with the number of requests. This feature primarily exists to write internal
Moray tests.

##### <a name="option-no_count">no_count</a>

A boolean which, when set to `true`, makes Moray not include a `_count` property
on each record sent with the response. Default is `false`.

The purpose of this option is to avoid performing a potentially expensive
`COUNT` query in addition to the query that fetches data records.

##### <a name="option-nolimit">noLimit</a>

A boolean which, when set to `true` and `limit` is not set, makes Moray not have
a maximum number of records processed by the request. Default is `false`.

Using this option is __considered dangerous__. The design center of Moray is
around short-lived requests, and there are serious consequences in PostgreSQL
for leaving connections open in transactions for extended periods while other
transactions are making changes.

##### <a name="option-offset">offset</a>

A number which indicates the offset at which Moray starts processing records
that would be returned by the same request with no `offset`. Default is `0`.

##### <a name="option-requireonlinereindexing">requireOnlineReindexing</a>

When passing `requireOnlineReindexing: true`, the client will make sure that
the server supports safely reading the contents of a reindexing bucket. Recent
versions of the server will always behave correctly here. This option is to
allow clients to make certain that they receive an `UnhandledOptionsError`
instead of having their data corrupted if they suspect that they are talking
to an older server.

##### <a name="option-req_id">req_id</a>

A string that can be used to track a request. Default is an automatically
generated V4 UUID.

##### <a name="option-sort">sort</a>

An object or an array of objects with the following properties:

* `attribute`: a required property of type string representing a field name on
  which to sort the result

* `order`: an optional property of type string that is either `ASC` or `DESC` to
  sort with ascending or descending order respectively. Default value is `ASC`.

Default value is `undefined`.

##### <a name="option-timeout">timeout</a>

A number that represents the delay in milliseconds that the request waits on a
reply from its underlying Postgres query before it errors with a
`QueryTimeoutError`. Default is `30000` (30 seconds).

Using this option is __strongly discouraged__. For identifying network failures,
TCP keep-alive, which is what the node-moray client library uses, is a better
fit. Otherwise, if the request hasn't completed, then the database is stuck
processing that request, and applications cannot generally do anything safely
except to wait (or possibly raise an alarm). Moreover, expiration of this
timeout does not cancel the underlying PostgreSQL query and the corresponding
PostgreSQL resources remain in use until the query ultimately does complete.


# Buckets

## CreateBucket

Creates a new bucket given a name and a `config` object.  The `config` object
defines an `index` section and optionally a `pre`, `post` array of trigger
functions.  Also, note that buckets can be versioned, such that subsequent
updates will fail if the version rolls backwards.

### API

A "fully loaded" config (without post triggers) would look like this:

    var cfg = {
        index: {
            email: {
                type: "string",
                unique: true
            },
            userid: {
                type: "number",
                unique: true
            },
            ismanager: {
                type: "boolean"
            }
        },
        pre: [
            function enforceEmail(req, cb) {
                if (!req.value.email)
                    return (cb(new Error('email is required')));

                return (cb());
            },
            function setUID(req, cb) {
                req.value.userid = Math.floor(Math.random() * 100001);
                cb();
            }
        ],
        options: {
            version: 1
        }
    }

    client.createBucket('foo', cfg, function (err) {
        assert.ifError(err);
    });

### Inputs

| Field    | Type     | Description                                     |
| -------- | -------- | ----------------------------------------------- |
| name     | string   | globally unique name for the bucket             |
| config   | object   | configuration (indexes/pre/post) for the bucket |
| options  | object   | any optional parameters, see below              |
| callback | function | only argument is `err`                          |

#### Options

- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `InvalidBucketConfigError`
* `InvalidBucketNameError`
* `InvalidIndexDefinitionError`
* `NoDatabasePeersError`
* `NotFunctionError`

Plus any currently unhandled Postgres Errors (such as relation already exists).

### CLI

To add that config above, you can use this invocation (`-s says wait for stdin`):

    $ putbucket -s foo < test.json

Or if you're not using triggers (more common, actually):

    $ putbucket -u email -u userid:number -i ismanager:boolean foo

Note there is no `createbucket` command; `putbucket` wraps up `CreateBucket` and
`UpdateBucket`.

## GetBucket

Returns the configuration for the bucket only.  There really are no options here
besides the name of the bucket you want to fetch (and of course request\_id).

### API

     client.getBucket('foo', function (err, bucket) {
        assert.ifError(err);
        console.log(util.inspect(bucket));
    });

### Inputs

| Field    | Type     | Description                         |
| -------- | -------- | ----------------------------------- |
| name     | string   | globally unique name for the bucket |
| options  | object   | any optional parameters, see below  |
| callback | function | only argument is `err`              |

#### Options

- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `BucketNotFoundError`
* `NoDatabasePeersError`

### CLI

    $ getbucket foo

    {
      "name": foo",
      "index": {
        "email": {
          "type": "string",
          "unique": true
        },
        "userid": {
          "type": "number",
          "unique": true
        },
        "ismanager": {
          "type": "boolean"
        }
      },
      "pre": [
        "function enforceEmail(req, cb) {
           if (!req.value.email) {
             return (cb(new Error('email is required')));
           }
           return (cb());
         }",
        "function setUID(req, cb) {
          req.value.userid = Math.floor(Math.random() * 100001);
          cb();
        }"
      ],
      "post": [],
      "mtime": "2012-07-31T19:43:32.268Z"
    }

Note the `\n's` inside the `pre` functions were inserted for readability in this
document; they were not actually returned, and are not legal JSON.

## ListBuckets

Returns the configuration for all buckets.

### API

     client.listBuckets(function (err, buckets) {
        assert.ifError(err);
        console.log(util.inspect(buckets));
     });

### Inputs

| Field    | Type     | Description                        |
| -------- | -------- | ---------------------------------- |
| options  | object   | any optional parameters, see below |
| callback | function | only argument is `err`             |

#### Options

- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `NoDatabasePeersError`

### CLI

    $ listbuckets
    [{
      "name": foo",
      "index": {
        "email": {
          "type": "string",
          "unique": true
        },
        "userid": {
          "type": "number",
          "unique": true
        },
        "ismanager": {
          "type": "boolean"
        }
      },
      "pre": [
        "function enforceEmail(req, cb) {
           if (!req.value.email) {
             return (cb(new Error('email is required')));
           }
           return (cb());
         }",
        "function setUID(req, cb) {
          req.value.userid = Math.floor(Math.random() * 100001);
          cb();
        }"
      ],
      "post": [],
      "mtime": "2012-07-31T19:43:32.268Z"
    }]

Note the `\n's` inside the `pre` functions were inserted for readability in this
document; they were not actually returned, and are not legal JSON.

## UpdateBucket

Updates a bucket given a name and a `config` object.  Basically, looks exactly
like `CreateBucket`.  The only reason we have `create|update bucket` and not
`putBucket` is so that users can selectively _not_ stomp all over a bucket if
they don't intend to (note there is a `putBucket` API in the client).

The `version` param in options is required if you are using versioned buckets,
and an error will be thrown if the version you are sending is `<=` than the
version in the database.

Note that if you *add* new indexes via `updateBucket`, then any *new* data will
be indexed accordingly, but _old_ data will not.  See the ReindexObjects
function for a method to update old rows. (A bucket version _must_ be specified
for `reindexObjects` to work.)

Also, note that all operations involving the bucket configuration in Moray will
use a cached copy of the bucket configuration, so it may take a few minutes for
your update to take effect.  This caching can be overridden during subsequent
operations by setting the `noBucketCache` option to true.

### API

    var cfg = {
        index: {
            email: {
                type: "string",
                unique: true
            },
            userid: {
                type: "number",
                unique: true
            }
        },
        pre: [],
        post: []
    }

    client.updateBucket('foo', cfg, function (err) {
        assert.ifError(err);
    });

Or alternatively:

    client.putBucket('foo', cfg, function (err) {
        assert.ifError(err);
    });

### Inputs

| Field    | Type     | Description                                     |
| -------- | -------- | ----------------------------------------------- |
| name     | string   | globally unique name for the bucket             |
| config   | object   | configuration (indexes/pre/post) for the bucket |
| options  | object   | any optional parameters, see below              |
| callback | function | only argument is `err`                          |

#### Options

- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `BucketNotFoundError`
* `InvalidBucketConfigError`
* `InvalidIndexDefinitionError`
* `NoDatabasePeersError`
* `NotFunctionError`

### CLI

    $ putbucket -s foo < test.json
    $ putbucket -u email -u userid:number foo

## DeleteBucket

Deletes a bucket, *and all data in that bucket!*

### API

    client.delBucket('foo', function (err) {
        assert.ifError(err);
    });

### Inputs

| Field    | Type     | Description                         |
| -------- | -------- | ----------------------------------- |
| name     | string   | globally unique name for the bucket |
| options  | object   | any optional parameters, see below  |
| callback | function | only argument is `err`              |

#### Options

- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `BucketNotFoundError`
* `NoDatabasePeersError`

### CLI

    $ delbucket foo

# Objects

With the exception of `ReindexObjects`, the object-related methods in Moray use
a cache for bucket information.  When a bucket is updated, say to alter its
index columns, stale data will persist in the bucket cache for up to 300
seconds.  For object operations that absolutely require current bucket
information, setting the `noBucketCache` option will bypass that cache.

## PutObject

Creates or overwrites an object given a bucket, key and value.  The bucket must
obviously already exist, and the key can be any string at all.  The value must
be a JS object of any form that can be run through `JSON.stringify()`.  Any keys
in the object that match the names of those defined in the bucket's `index`
section are indexed, and available for searching later.

As far as options, besides the usual `req_id`, notably you can set the flag
`etag`, which will make moray _atomically_ test/set your new value (this is
useful if you're implementing something like a lock semantic).  To ensure that
you're "creating" a record that never existed, set etag to `null`, otherwise set
it to the value of the last known version of `etag` you have.  If the etag differs
you'll receive an `EtagConflictError` which, in addition to your being able to
switch on the name, is decorated with the info you need; a sample `.toString()`
looks like:

    {
        name: 'EtagConflictError',
        message: '...',
        context: {
            bucket: 'moray_unit_test_2423db7',
            key: '<KEY>',
            expected: 'ABCDEFGH',
            actual: 'B21548FF'
        }
    }

If the update has been successfully applied, then the new `etag` will be
returned to the client.

### API

    var data = {
        email: '<EMAIL>',
        ismanager: false
    };
    client.putObject('foo', 'mcavage', data, function (err, res) {
        assert.ifError(err);
        console.log(res.etag);
    });

### Inputs

| Field    | Type     | Description                             |
| -------- | -------- | --------------------------------------- |
| bucket   | string   | bucket to write this key in             |
| key      | string   | bucket to write this key in             |
| value    | object   | free-form JS object                     |
| options  | object   | any optional parameters, see below      |
| callback | function | only argument is `err`                  |

#### Options

- [etag](#option-etag)
- [req_id](#option-req_id)
- [timeout](#option-timeout)

### Errors

* `BucketNotFoundError`
* `EtagConflictError`
* `NoDatabasePeersError`
* `UniqueAttributeError`
* `InvalidIndexTypeError`

Plus any currently unhandled Postgres Errors (such as relation already exists).
{'fraction_non_alphanumeric': 0.11039853412734768, 'fraction_numerical': 0.004823367734633936, 'mean_word_length': 2.663573543928924, 'pattern_counts': {'":': 52, '<': 59, '<?xml version=': 0, '>': 55, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7005707', 'n_tokens_mistral': 10329, 'n_tokens_neox': 9792, 'n_words': 4289}