projection-utils
A set of utilities for working with MongoDB-style projections.
Notably, this project exposes a ProjectionFieldSet
class that tracks, merges,
and intersects multi-level projections.
We do not support symmetric or asymmetric diffing of field sets, as the
semantics are not well-defined on mongo projections. A field set that contains
users
minus a field set that contains users.accessToken
would need new
syntax to represent the fields under users
that aren't accessToken
, or would
need knowledge of all existant fields under the users
subdocument. It's better
to handle this yourself, using intersect
, and a whitelist of permitted fields.
ProjectionFieldSet
Basic usage:
const permittedFields = ProjectionFieldSet; const desiredFields = ProjectionFieldSet; // The fields we want, where they're permitted.const selectedFields = permittedFields; // Add fields that we need for server-side business logic.const mandatoryFields = ProjectionFieldSet; const queryFields = selectedFields;const projection = queryFields;// => {'users.id': 1, 'users.email': 1, share: 1, internalVersion: 1}
Constructor usage:
// Equivalent to the first fromDotted invocation in the previous example.const permittedFields = 'users' 'id' 'users' 'email' 'share' 'content';
Iterate over paths:
for const path of permittedFields // path is the array containing the parts of the path, e.g.: // ['users', 'email'] // Or just convert to an Array:const fields = Array;
Enumerate dot-joined paths:
const dotJoined = Array;// => ['users.id', 'users.email', 'share', 'internalVersion']
Check for field containment, and partial field containment:
queryFields;// => false, because only some of the fields in users are included // equivalent to the abovequeryFields; // produces the set of fields that are included under the users fieldArray;// => [['users', 'id'], ['users', 'email']] Array;// => ['users.id', 'users.email'] // both produce no itemsArray;Array;// => [] // exclude the users prefixArray;// => [['id'], ['email']] Array;// => ['id', 'email']
Explicitly expand the set of fields:
// Add users.name to queryFields. Unlike intersect and union, this mutates the// ProjectionFieldSet instead of making a new instance.queryFields;queryFields;// => {'users.id': 1, 'users.email': 1, 'users.name': 1, share: 1, internalVersion: 1} // Expand queryFields to include all fields of users (even accessToken - take// care when ordering operations on ProjectionFieldSets, as an intersect won't// forbid a set of fields being added to the produced ProjectionFieldSet.queryFields;queryFields;// => {users: 1, share: 1, internalVersion: 1}
Note that field sets can be singular. Unioning with a singular value yields a singular value, and intersecting with a singular value yields the non-singular value. For example:
// This is distinct from new ProjectionFieldSet([]) (and// new ProjectionFieldSet()), which yield an empty fieldset, rather than a// singular fieldset.const singular = ; singular;// => copy of singular singular;// => copy of singular singular;// => copy of singular singular;// => copy of mandatoryFields const empty = ; empty;// => copy of empty empty;// => copy of empty empty;// => copy of mandatoryFields empty;// => copy of empty singular;// => copy of singular singular;// => copy of empty