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.
Option | Type | Default | Description |
---|---|---|---|
class | typeof Entity | Entity | The Entity class to query. |
limit | number | undefined | The limit of entities to be returned. Not needed when using getEntity , as it always returns only one. |
offset | number | 0 | The offset from the first matching entity, in order, to start retrieving. |
reverse | boolean | false | If 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. |
source | string | undefined | Will be 'client' if the query came from a REST request or the PubSub server. (Mainly used in Tilmeld for access control.) |
skipCache | boolean | false | If true, Nymph will skip the cache and retrieve the entity from the DB. |
skipAc | boolean | false | If 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.
Type | Name | Description |
---|---|---|
& | And | All clauses in the selector must match. |
| | Or | At least one clause in the selector must match. |
!& | Not And | All clauses in the selector must not match. |
!| | Not Or | At 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.
Property | Description | Example | Works On |
---|---|---|---|
guid | The entity's GUID is equal. | {type: '&', guid: '790229ae527f1511b3120b71'} | entity.guid = '790229ae527f1511b3120b71' |
tag | The entity has the tag. | {type: '&', tag: 'foobar'} | entity.$addTag('foobar') |
defined | The named property is not undefined. | {type: '&', defined: 'foo'} | entity.foo = 0 |
truthy | The named property evaluates to true. | {type: '&', truthy: 'foo'} | entity.foo = 1 |
equal | The named property is defined and equals the value (their JSON strings are identical). | {type: '&', equal: ['foo', 0]} | entity.foo = 0 |
contain | The named property is an array that contains the value. | {type: '&', contain: ['foo', 'bar']} | entity.foo = ['bar', 'baz'] |
match | The named property matches. Uses POSIX RegExp. Case sensitive. Must *not* be surrounded by any delimiters. | {type: '&', match: ['foo', 'bar.*z']} | entity.foo = 'foobarbaz' |
imatch | The named property matches. Uses POSIX RegExp. Case insensitive. Must *not* be surrounded by any delimiters. | {type: '&', imatch: ['foo', 'BaR.*Z']} | entity.foo = 'foobarbaz' |
like | The named property matches. Uses % for variable length wildcard and _ for single character wildcard. Case sensitive. | {type: '&', like: ['foo', 'f%bar_az']} | entity.foo = 'foobarbaz' |
ilike | The named property matches. Uses % for variable length wildcard and _ for single character wildcard. Case insensitive. | {type: '&', ilike: ['foo', 'F%bAr_aZ']} | entity.foo = 'foobarbaz' |
gt | The named property is greater than the value. | {type: '&', gt: ['foo', 5]} | entity.foo = 6 |
gte | The named property is greater than or equal to the value. | {type: '&', gte: ['foo', 6]} | entity.foo = 6 |
lt | The named property is less than the value. | {type: '&', lt: ['foo', 7]} | entity.foo = 6 |
lte | The named property is less than or equal to the value. | {type: '&', lte: ['foo', 6]} | entity.foo = 6 |
ref | The named property is the entity or contains the entity. | {type: '&', ref: ['foo', '790229ae527f1511b3120b71']} | entity.foo = await Entity.factory('790229ae527f1511b3120b71') |
qref | The 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']}) |
selector | A 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 %']
]
}
]
]
}
);