npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

projection-utils

v1.1.0

Published

Utilities to work with projections (e.g. mongo)

Downloads

39,900

Readme

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.fromDotted(
  ['users.id', 'users.email', 'share', 'content']);

const desiredFields = ProjectionFieldSet.fromDotted(
  ['users', 'users.accessToken', 'share', 'invalid']);

// The fields we want, where they're permitted.
const selectedFields = permittedFields.intersect(desiredFields);

// Add fields that we need for server-side business logic.
const mandatoryFields = ProjectionFieldSet.fromDotted(
  ['internalVersion']);

const queryFields = selectedFields.union(mandatoryFields);
const projection = queryFields.toMongo();
// => {'users.id': 1, 'users.email': 1, share: 1, internalVersion: 1}

Constructor usage:

// Equivalent to the first fromDotted invocation in the previous example.
const permittedFields = new ProjectionFieldSet([
  ['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.from(permittedFields);

Enumerate dot-joined paths:

const dotJoined = Array.from(queryFields.toDotted());
// => ['users.id', 'users.email', 'share', 'internalVersion']

Check for field containment, and partial field containment:

queryFields.contains(['users']);
// => false, because only some of the fields in users are included

// equivalent to the above
queryFields.containsDotted('users');

// produces the set of fields that are included under the users field
Array.from(queryFields.get(['users']));
// => [['users', 'id'], ['users', 'email']]

Array.from(queryFields.getDotted('users'));
// => ['users.id', 'users.email']

// both produce no items
Array.from(queryFields.get(['invalid']));
Array.from(queryFields.getDotted('invalid'));
// => []

// exclude the users prefix
Array.from(queryFields.get('users', false));
// => [['id'], ['email']]

Array.from(queryFields.getDotted('users', false));
// => ['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.widen(['users', 'name']);
queryFields.toMongo();
// => {'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.widen(['users']);
queryFields.toMongo();
// => {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 = new ProjectionFieldSet([[]]);

singular.union(singular);
// => copy of singular

singular.intersect(singular);
// => copy of singular

singular.union(mandatoryFields);
// => copy of singular

singular.intersect(mandatoryFields);
// => copy of mandatoryFields

const empty = new ProjectionFieldSet([]);

empty.union(empty);
// => copy of empty

empty.intersect(empty);
// => copy of empty

empty.union(mandatoryFields);
// => copy of mandatoryFields

empty.intersect(mandatoryFields);
// => copy of empty


singular.union(empty);
// => copy of singular

singular.intersect(empty);
// => copy of empty