6.1.0 • Published 6 years ago

datastore2 v6.1.0

Weekly downloads
5
License
MIT
Repository
github
Last release
6 years ago

Datastore2

  • Atomic transactions
    • Either all entities get commits, or none at all.
  • Requires use of pure functions to modify entities
    • Take in fetched entities, return modified entities
    • Return Promise.resolve(entities) to proceed transaction.
    • Return Promise.reject(...whatever) to cancel transaction, and pass whatever to your catch.
  • Entity locking with dreadlocks
    • Transaction will be delayed if one of its entities are involved in other transactions
    • Ensures isolation and consistency of involved entities in transactions.
  • Simplified entity search from supplied filters.
    • Saves you the hassle of manually crafting queries.
  • Simplified entity creation from UUIDv4 key name.
    • Also uses transactions, so users don't unknowingly write on the same key name.
  • Simplified queries with endCursor and query hash provided for caching.
    • Easier search, and unique hash for each query instance for easier caching.

Changelog

  • 6.x
    • Added caching support
    • Added cache option globalMaxSize & namespacedMaxSize
    • Added export const lruGlobal
    • Added setKey & setKind parameter type checks
  const datastoreOpts = {
    projectId: 'project-name-goes-here'
  };
  const cacheOpts = {
    globalMaxSize: 10, // ten global items only, REQUIRED
    namespacedMaxSize: 10 // ten items per namespaced cache only, REQUIRED
  };
  const { lruGlobal, Query, } = require('./index')(datastoreOpts, cacheOpts);
  new Query().runQuery(true); // blind caching, by sha256 hash of query.
  new Query().runQuery('namespace1'); // namespaced caching, still by sha256 hash of query.
  lruGlobal.delete('namespace1'); // clears a namespace
  lruGlobal.clear(); // clears all global namespaces and blindly cached values
  • 5.x
    • Added TypeError on wrongly parameterized Key initialization, only accepts typeof 'string' or 'number'
    • Improved exception handling, now handles resolve, reject and thrown exceptions.
    • Added debug logging capability.
    • Inclusion of entity namespace in hashes.
    • Added a check that ensures executor function return a promise.
    • Added a check that ensures the returned promise passes an object argument.
  • 4.x
    • Improved exception handling.
  • 3.x
    • Breaking changes
      • Entity.update changed to Entity.upsert
      • Entity.useValidator changed to Entity.setValidator
  • 2.x
    • Added Entity.delete()
    • Added Query.offset(x)
    • Use of dreadlocks and use of pure function as executor function in Transactions..
      • Fixes data integrity & consistency problems on same-entity transactions that commit at the same time.
    • Added Query result hashing for caching purposes.
      new Query('Persons')
        .runQuery(({ entities, keys, endCursor, hash }) => {
          console.log(hash);
        });
    • Added AVA tests
      npm test
    • Added Entity.setValidator support. This allows you to validate your data before Entity upsert and merge calls. Just return Promise.resolve() to proceed, or Promise.reject(whatever) to cancel.
      const Joi = require('joi');
      entity3.setValidator((data) => {
        const schema = {
          first_name: Joi.string().alphanum(),
          last_name: Joi.string().alphanum(),
          email: Joi.string().email()
        };
        const result = Joi.validate(data, schema);
        if (result.error !== null) {
          return Promise.reject(result.error.details);
        } else {
          return Promise.resolve();
        }
      });
  • 1.x
    • Transaction class
    • Queue class
    • Entity class
    • Key class

Examples:

Setup:
  • With NPM:
npm install datastore2 --save
  • With YARN:
yarn add datastore2
  • Script:
// Set datastore constructor opts
const opts = {
  projectId: 'projectNameGoesHere'
};
// Destructure
const Datastore2 = require('datastore2');
const { Key, Transaction, Entity, Query } = Datastore2(opts);
Entity from UUIDv4 key name:
  • Notes:
    • setKind() must be called first.
let Alice = new Entity();
Alice.setKind('Persons').fromUUID()
  .then(() => {
    console.log(Alice);
    console.log(Alice.key);
  });
// console.log(Alice);
Entity {
  kind: 'Persons',
  key:
   Key {
     namespace: undefined,
     name: 'a71757ec-2565-4c0e-ab2f-0df909eba87b',
     kind: 'Persons',
     path: [Getter] } }

// console.log(Alice.key);
Key {
  namespace: undefined,
  name: 'a71757ec-2565-4c0e-ab2f-0df909eba87b',
  kind: 'Persons',
  path: [Getter] }
Entity data upsert:
  • Notes:
    • Overwrites existing entity data.
let Alice = new Entity();
Alice.setKind('Persons').fromUUID()
  .then(() => {
    return Alice.upsert({
      first_name: 'Alice'
    });
  });
Entity data merge:
  • Notes:
    • Merges new data with existing entity data
    • Uses Object.assign
let Alice = new Entity();
Alice.setKind('Persons').fromUUID()
  .then(() => {
    return Alice.merge({
      last_name: 'Alice'
    });
  });
Creating a key:
  • Arguments:
    • kind
    • keyName
  • Notes:
    • Strictly applies String() to keyName.
    • This is because entities with Numerical ID's as keyName eventually creates unpredictability once their keyName is passed between servers and browsers.
    • There are times these Numerical ID's are received by browsers as Integer but then sent back as String.
    • Entities get Numerical ID's when they are created using the Google Cloud Datastore Console and you skip assigning a Custom Name as the Entity's Key Identifier.
let aliceKey = Key('Persons', 'alice-key-name');
Entity from key:
  • Notes:
    • No need to call setKind() first since entity kind is already supplied in your Key.
    • This flow is ideal for direct upserts and merges.
    • If you intend to read the data first before performing mutations, using the Transaction class is highly recommended.
let aliceKey = Key('Persons', 'alice-key-name');
let Alice = new Entity();
Alice.setKey(aliceKey)
  // .upsert() or .merge() goes here
Entity from filters:
  • Notes:
    • setKind() must be called first.
let Alice = new Entity();
Alice
  .setKind('Persons')
  .fromFilters(
    ['first_name', '=', 'Alice'],
    ['last_name', '=', 'Park']
  )
  .then(() => {
    console.log(Alice.key);
  })
  .catch((e) => console.log('error:', e));
Key {
  namespace: undefined,
  name: '41719603-268f-439b-a6a8-fcc15e34b380',
  kind: 'Persons',
  path: [Getter] }
Basic transaction:
  • Flow Notes:
    • new Transaction() creates a Transaction.
    • Then you supply your mapped keys to .keys()
    • You call .exec() to supply your executor function.
    • Executor function accept one argument:
      • entities - the entities involved in transaction, modify them DIRECTLY as you wish.
    • Executor function must return Promise
      • Promise.resolve() allows transaction to proceed.
      • Promise.reject()discontinues transaction, rollback.
  • Example Notes:
    • Alice.balance is 50
    • Bob.balance is 0
    • Alice sends transactionAmount (50) to Bob.
const aliceKey = Key('Persons', 'alice-key-name')
const bobKey = Key('Persons', 'bob-key-name')

const transactionAmount = 50;

new Transaction()
  .keys({ alice: aliceKey, bob: bobKey })
  .exec((entities) => {
    entities.alice.balance -= transactionAmount;
    entities.bob.balance += transactionAmount;
    if (entities.alice.balance < 0) {
      return Promise.reject("Alice can't send that much.");
    } else {
      return Promise.resolve(entities);
    }
  });
Entity delete:
const aliceKey = Key('Persons', 'alice-key-name')

let Alice = new Entity().setKey(aliceKey);
Alice.delete()
  .then(() => console.log('Alice deleted!'));
Entity queries:
  • Notes:
    • Required kind must be supplied to constructor, while endCursor is optional.
      • new Query() is invalid
      • new Query('Persons') is valid
      • new Query('Persons', 'endcursorxyz') is valid.
    • .order() method is replaced with .ascend() and .descend() for readability.
    • As per Google Datastore docs on Queries, when you use an inequality filter to a column, you must sort that same column BEFORE you sort other columns.
      • In which case calling .ascend('age') or .descend('age') after calling .filter('age', '>', 20) is the right thing to do.
  • Supported methods:
    • .ascend(col)
      • col is the column you want to ascend.
    • .descend(col)
    • col is the column you want to descend.
    • .select(fields)
      • fields - array of strings
    • .filter(col, operator, val)
      • col is column, for example: 'first_name'
      • operator, for example: '='
      • val is value, for example: 'Alice'
    • .offset(val)
      • val is value, for example: 5
    • .limit(limit)
      • limit is integer, for example: 1
    • .runQuery()
      • Runs the query.
      • Returns object
        • Has entities, keys, endCursor and hash.
  new Query('Persons')
    .filter('first_name', '=', 'Alice')
    .limit(1)
    .runQuery()
    .then(({ entities, keys, endCursor, hash }) => {

      });

License

MIT, see LICENSE file.

6.1.0

6 years ago

5.3.0

6 years ago

5.2.0

6 years ago

5.1.0

6 years ago

5.0.1

6 years ago

4.1.0

6 years ago

4.0.0-alpha-1

6 years ago

3.0.0

6 years ago

2.7.2

6 years ago

2.7.1

6 years ago

2.7.0

6 years ago

2.6.0

6 years ago

2.5.0

6 years ago

2.4.0

6 years ago

2.3.0

6 years ago

2.2.0

6 years ago

2.1.1

6 years ago

2.1.0

6 years ago

1.3.0

6 years ago

1.2.0

6 years ago

1.1.0

6 years ago

1.0.0

6 years ago