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

@wootapa/polygraph

v1.0.1

Published

Tests objects with queries constructed using logical and comparison operators.

Downloads

2

Readme

Polygraph

Polygraph is a library for testing objects given a query that is constructed using logical and comparison operators.

const jobs = [...];
const query = and().gt('salary', 20000).any('title', ['Developer', 'Astronaut']).done();
const interestingJobs = jobs.filter(job => query.evaluate(job));

Polygraph is quite flexible and can:

  • Construct complex queries easily
  • Combine many queries into one
  • Serialize and store it for later, or maybe push to a webworker.

Installation

New browsers, bundlers and node14+ (es):

$ npm install --save @wootapa/polygraph

For old browsers and node12 (umd):

<script src="https://unpkg.com/@wootapa/polygraph"></script>
// polygraph.and() ...

Methods

Constructors

  • and() - Creates new query with a root and logical operator.
  • or() - Creates new builder with a root or logical operator.
  • not() - Creates new builder with a root not logical operator.
  • fromJson(json) - Creates new builder from a serialized builder.
  • define(alias, function) - Defines a new operator.

Logical operators

  • and() - True if all child operators are true.
  • or() - True if one of the child operators are true.
  • not() - True if all child operators are false.

Comparison operators

  • equals(key, value) - True if object[key] equals to value. Alias=eq.
  • isNull(key) - True if object[key] is null or undefined. Alias=isnull.
  • greaterThan(key, value) - True if object[key] is greater than value. Alias=gt.
  • greaterThanEquals(key, value) - True if object[key] is greater or equal to value. Alias=gte.
  • lessThan(key, value) - True if object[key] is less than value. Alias=lt.
  • lessThanEquals(key, value) - True if object[key] is less or equal to value. Alias=lte.
  • like(key, value) - True if object[key] is like value (case sensitive). Use * as wildcard. Alias=like.
  • ilike(key, value) - True if object[key] is like value (case insensitive). Use * as wildcard. Alias=ilike.
  • any(key, values[]) - True if object[key] equals to any of the values.

Evaluation

  • evaluate(object) - Evaluates object. True if object passed all operators.

Logical traversal

  • done() - Moves up to root logical.
  • up() - Moves up to parent logical.
  • down() - Moves to first logical child.
  • next() - Moves to next logical sibling.
  • prev() - Moves to previous logical sibling.

Other

  • asJson() - Serializes to json. Restore with fromJson.
  • asTree() - Returns a more human readable tree.
  • clone() - Returns a deeply cloned builder.
  • clear() - Clears all operators and below.
  • operator(alias, key, value?, opts?) - Use operator by its alias.
  • op(alias, key, value?, opts?) - shorthand for above.
  • addBuilder(builder) - Adds another builder.
  • getReport() - Returns a report with statistics. Useful for finding the breaking operator or bottlenecks.
  • resetReport() - Reset statistics.
  • getKeysAndValues() - Returns keys and values for all comparison operators. This can be useful when restoring state to something (forms etc). If the same key has been used multiple times an array of values are returned.

An example

So maybe you have a bunch of movies and you want some good comedies.

const q = and().eq('category', 'comedy').gte('ranking', 6).done();

However, you just realized you have issues with some actors.

q.not().any('actor', ['Jim Carrey', 'Ben Stiller']).done();

Its better, but you heard there will be kids around.

q.eq('rating', 'G').done();

Also, you know your buddy likes the old movies better.

q.gt('year', new Date(1990,0)).lt('year', new Date(2000,0)).done();

In the end, this is the result.

const q = and()  // <- Will only return true if all children do
    .eq('category', 'comedy') // <- category must be comedy
    .gte('ranking', 6) // <- ranking must be greater than 6
    .not() // <- Will only return true if all children are false
        .any('actor', ['Jim Carrey', 'Ben Stiller']) // <- actors must not be these
        .up() // <- Moves up one level to 'and' operator
    .eq('rating', 'G') // <- rating must be G
    .gt('year', new Date(1990,0)) // <- year must be greater than 1990
    .lt('year', new Date(2000,0)) // <- year must be less than 2000
    .done() // <- moves to the root 'and' operator (which is unnecessary here but easy to forget)

const movies = [...];
const comedies = movies.filter(q.evaluate);

Custom operator

With define you can create your own operator. Supply an alias and a function that takes a value and returns a boolean.

define('divisibleby2', (value: number) => value % 2 === 0);

Then use it with the operator (or shorthand op) method.

and().op('divisibleby2', 'age').done().evaluate({ age: 20 }); // => true

The operator will survive serialization, but can break or have sideffects if you depend on this or some other state that might not be there after deserialization.

Aliases

All operators (except any) can be used with its alias, just as custom operators. It can be useful in situations where you simply want polygraph to figure out the operator for you. For example, the following is equivalent:

and().eq('name', 'Foo').gte('age', 20).done();
and().op('eq', 'name', 'Foo').op('gte', 'age', 20).done();

Objects

Polygraph tries to be somewhat intelligent, so the evaluating object can be a plain dict, have nested properties...or be a function. So, given the following:

const person = {
    name: {
        first: 'Nariyoshi',
        last: 'Miyagi'
    },
    age: () => 60
};

Nested properties will resolve.

and().eq('name.first', 'Nariyoshi').done().evaluate(person); // => true

Property functions also resolves.

and().gte('age', 50).done().evaluate(person); // => true

You can also pass a function and resolve values anyway you want. Key is passed as argument.

and().eq('name.first', 'Nariyoshi').done().evaluate(key => {
    if (key === 'name.first') {
        return person.name.first;
    }
}); // => true

Which then means you can have nonexistent properties if you want.

and().eq('isKarateMan', true).done().evaluate(key => {
    if (key === 'isKarateMan') {
        return person.name.first === 'Nariyoshi' && person.name.last === 'Miyagi';
    }
}); // => true