Entity Querying

The real power behind Nymph is the entity querying system.

Factory Method

The Entity class' factory method can take a GUID as an argument. You can provide other factory functions that can take things as well. For example, the User class in Tilmeld has a factoryUsername method that takes a username. The method will return a new entity if the queried entity is not found. You can determine if it was found by checking that its GUID is not null.

const baz = await FoobarBaz.factory(guid);
if (baz.guid == null) {
  console.error("Can't find the Foobar Baz!");
}

// Tilmeld's User class has a username factory function.
const cronUser = await User.factoryUsername('cron');
if (cronUser.guid == null) {
  console.error("Can't find the cron user!");
}

Nymph's Query Language

The powerful way of querying entities is Nymph's getEntities and getEntity methods. The first argument is an Options object.

OptionTypeDefaultDescription
classtypeof EntityEntityThe Entity class to query.
limitnumberundefinedThe limit of entities to be returned. Not needed when using getEntity, as it always returns only one.
offsetnumber0The offset from the first matching entity, in order, to start retrieving.
reversebooleanfalseIf true, entities will be retrieved from newest to oldest/largest to smallest (with regard to sort).
sort'cdate' | 'mdate' | string'cdate'How to sort the entities. Should be "cdate", "mdate", or the name of a property.
return'entity' | 'guid' | 'count''entity'What to return, the entities with their data, just the GUIDs, or just a count.
sourcestringundefinedWill be 'client' if the query came from a REST request or the PubSub server. (Mainly used in Tilmeld for access control.)
skipCachebooleanfalseIf true, Nymph will skip the cache and retrieve the entity from the DB.
skipAcbooleanfalseIf true, Tilmeld will not filter returned entities according to access controls. (If Tilmeld is installed.) (This is always set to false by the REST endpoint and PubSub server.)

Every argument following the Options is a Selector object. They contain clauses and a type. An entity must match each selector to be returned. There are four selector types, and they are defined on the type property.

TypeNameDescription
&AndAll clauses in the selector must match.
|OrAt least one clause in the selector must match.
!&Not AndAll clauses in the selector must not match.
!|Not OrAt least one clause in the selector must not match.

The other properties of the Selector are clauses. Clauses use the form name: value, or name: [value1, value2, ...]. They can be negated by prepending a bang (!) to the name, like '!name': value. A clause that has multiple values is considered as multiple clauses in terms of matching for "or" selectors.

PropertyDescriptionExampleWorks On
guidThe entity's GUID is equal.{type: '&', guid: '790229ae527f1511b3120b71'}entity.guid = '790229ae527f1511b3120b71'
tagThe entity has the tag.{type: '&', tag: 'foobar'}entity.$addTag('foobar')
definedThe named property is not undefined.{type: '&', defined: 'foo'}entity.foo = 0
truthyThe named property evaluates to true.{type: '&', truthy: 'foo'}entity.foo = 1
equalThe named property is defined and equals the value (their JSON strings are identical).{type: '&', equal: ['foo', 0]}entity.foo = 0
containThe named property is an array that contains the value.{type: '&', contain: ['foo', 'bar']}entity.foo = ['bar', 'baz']
matchThe named property matches. Uses POSIX RegExp. Case sensitive. Must *not* be surrounded by any delimiters.{type: '&', match: ['foo', 'bar.*z']}entity.foo = 'foobarbaz'
imatchThe named property matches. Uses POSIX RegExp. Case insensitive. Must *not* be surrounded by any delimiters.{type: '&', imatch: ['foo', 'BaR.*Z']}entity.foo = 'foobarbaz'
likeThe named property matches. Uses % for variable length wildcard and _ for single character wildcard. Case sensitive.{type: '&', like: ['foo', 'f%bar_az']}entity.foo = 'foobarbaz'
ilikeThe named property matches. Uses % for variable length wildcard and _ for single character wildcard. Case insensitive.{type: '&', ilike: ['foo', 'F%bAr_aZ']}entity.foo = 'foobarbaz'
gtThe named property is greater than the value.{type: '&', gt: ['foo', 5]}entity.foo = 6
gteThe named property is greater than or equal to the value.{type: '&', gte: ['foo', 6]}entity.foo = 6
ltThe named property is less than the value.{type: '&', lt: ['foo', 7]}entity.foo = 6
lteThe named property is less than or equal to the value.{type: '&', lte: ['foo', 6]}entity.foo = 6
refThe named property is the entity or contains the entity.{type: '&', ref: ['foo', '790229ae527f1511b3120b71']}entity.foo = await Entity.factory('790229ae527f1511b3120b71')
qrefThe named property is an entity that matches the query or contains an entity that matches the query.{type: '&', qref: ['foo', [{class: Entity}, {type: '&', equal: ['name', 'Foobar']}]]}entity.foo = await nymph.getEntity({class: Entity}, {type: '&', equal: ['name', 'Foobar']})
selectorA selector. (Keep in mind, you can also use an array of these, just like any other clause.){type: '&', selector: {type: '|', tag: ['foo', 'bar']}}entity.$addTag('bar')

The clauses "equal", "contain", "gt", "gte", "lt", and "lte" can also accept a third element. If value is null and the third element is a string, the third element will be used with Locutus' strtotime function to set the value to a relative timestamp. For example, the following selector will look for all entities that were created in the last day.

{
  type: '&',
  gte: ['cdate', null, '-1 day']
}

Querying Examples

So putting it all together, you can specify the options and selectors to find the exact entities you want.

Get the first FoobarBaz entity.

const entity = await nymph.getEntity({ class: FoobarBaz });

Get the latest FoobarBaz entity.

const entity = await nymph.getEntity({
  class: FoobarBaz,
  reverse: true
});

Get all baz tagged entities, using the FoobarBaz class.

const entities = await nymph.getEntities(
  {
    class: FoobarBaz
  },
  {
    type: '&',
    tag: 'baz'
  }
);

Get the five last created bar and baz tagged entities.

const entities = await nymph.getEntities(
  {
    class: FoobarBaz,
    reverse: true,
    limit: 5
  },
  {
    type: '&',
    tag: ['bar', 'baz']
  }
);

Get the five last modified bar and baz tagged entities.

const entities = await nymph.getEntities(
  {
    class: FoobarBaz,
    reverse: true,
    limit: 5,
    sort: 'mdate'
  },
  {
    type: '&',
    tag: ['bar', 'baz']
  }
);

Get the third page of sorted by name, baz tagged entities (if pages are 5 entities long).

const entities = await nymph.getEntities(
  {
    class: FoobarBaz,
    limit: 5,
    offset: 10,
    sort: 'name'
  },
  {
    type: '&',
    tag: ['baz']
  }
);

Get baz tagged entities with names.

const entities = await nymph.getEntities(
  {
    class: FoobarBaz
  },
  {
    type: '&',
    tag: 'baz',
    defined: 'name'
  }
);

Get baz tagged entities without names.

const entities = await nymph.getEntities(
  {
    class: FoobarBaz
  },
  {
    type: '&',
    tag: 'baz',
    '!defined': 'name'
  }
);

Get baz tagged entities without names or bar tagged entities with names.

const entities = await nymph.getEntities(
  {
    class: FoobarBaz
  },
  {
    type: '|',
    selector: [
      {
        type: '&',
        tag: 'baz',
        '!defined': 'name'
      },
      {
        type: '&',
        tag: 'bar',
        defined: 'name'
      }
    ]
  }
);

Get baz tagged entities with either first names or last names.

const entities = await nymph.getEntities(
  {
    class: FoobarBaz
  },
  {
    type: '&',
    tag: 'baz'
  },
  {
    type: '|',
    defined: ['firstName', 'lastName']
  }
);

Get baz tagged entities created in the last day.

const entities = await nymph.getEntities(
  {
    class: FoobarBaz
  },
  {
    type: '&',
    tag: 'baz',
    gt: ['cdate', null, '-1 day']
  }
);

Get baz tagged entities with names, who either make only up to 8 dollars pay or are under 22.

const entities = await nymph.getEntities(
  {
    class: FoobarBaz
  },
  { type: '&', tag: 'baz', defined: 'name' },
  {
    type: '!|', // at least one must be false
    gte: ['age', 22],
    gt: ['pay', 8]
  }
);

Get baz tagged entities named Clark, James, Chris, Christopher, Jake, or Jacob.

const entities = await nymph.getEntities(
  { class: FoobarBaz },
  { type: '&', tag: 'baz' },
  {
    type: '|',
    equal: [
      ['firstName', 'Clark'],
      ['firstName', 'James']
    ],
    match: [
      ['firstName', '^Chris(topher)?$'],
      ['firstName', '^Ja(ke|cob)$']
    ]
  }
);

Get baz tagged entities that belong to any user named "John" or "James".

const entities = await nymph.getEntities(
  { class: FoobarBaz },
  {
    type: '&',
    tag: 'baz',
    qref: [
      'user',
      [
        { class: User },
        {
          type: '|',
          like: [
            ['name', 'John %'],
            ['name', 'James %']
          ]
        }
      ]
    ]
  }
);
Previous: Creating Entities Next: Subscribing to Queries