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

knight-criteria

v2.2.1

Published

A data structure to describe criteria for CRUD operations on a data store

Downloads

6

Readme

Knight Criteria by Coderitter

A data structure to describe criteria for operations on a data store. They can be serialized to JSON and thus are fitting for API calls.

Related packages

The package knight-orm offers a criteria-based interface to load entities from databases.

If you just want to match plain JavaScript objects against criteria you can use knight-criteria-matcher. There is also an in-memory object database knight-object-db which uses criteria for queries.

Install

npm install knight-criteria

Overview

Criteria work like this.

import { Criteria } from 'knight-criteria'

let criteria: Criteria = {
  name: 'Josa'
}

Which result in an SQL query looking like this.

SELECT * FROM person WHERE name = 'Josa`

Criteria object

Here is an overview of what you can do with a criteria object.

import { Criteria } from 'knight-criteria'

let criteria: Criteria = {
  // a simple value which will default to the operator =
  // WHERE id = 1
  id: 1,

  // explicitely define an operator
  // =, ==, !=, <>, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN
  // WHERE name LIKE '%ert%'
  name: { '@operator': 'LIKE', '@value': '%ert%' },

  // use an array of values which will equal to an SQL IN operator
  // WHERE job IN ('student', 'teacher')
  job: [ 'student', 'teacher' ],

  // use an array of explicitely defined operators which result them to be OR connected
  // WHERE (age < 20 OR age > 30)
  age: [{ '@operator': '<', '@value': 20 }, { '@operator': '>', '@value': 30 }],

  // use an array of explicitely defined operators and explicitely define a connecting logical operator
  // WHERE (age > 20 AND age < 30)
  age: [{ '@operator': '>', '@value': 20 }, 'AND', { '@operator': '<', '@value': 30 }],

  // order by a particular field in ascending direction
  '@orderBy': 'job',

  // order by multiple fields in ascending direction
  '@orderBy': [ 'job', 'age' ],

  // order by one field and specify the direction explicitly
  '@orderBy': { field: 'job', direction: 'DESC' },

  // order by multiple fields and specify their directions explicitly
  '@orderBy': [{ field: 'job', direction: 'DESC' }, { field: 'age', direction: 'DESC' }],

  // limit the number of results
  '@limit': 10,

  // offset the results
  '@offset': 5
}  

Relationships

You can also define criteria on relationships.

let criteria: Criteria = {
  // only include people which have at least one email address from Google
  emails: {
    address: { '@operator': 'LIKE', '@value': '%@gmail.com' }
  }

  // only include people which have at least one email address from Google
  // while also loading these emails
  emails: {
    '@load': true,
    address: { '@operator': 'LIKE', '@value': '%@gmail.com' }
  }  

  // include all people and load any email
  emails: { '@load': true }

If the criteria is translated into an SQL query it will use a JOIN and looks like this.

SELECT * FROM person JOIN email ON person.id = email.person_id WHERE email.address LIKE '%@gmail.com'

You can also define relationship criteria which will only apply to the entities in the relationship itself but not to the entities owning the relationship.

  // include all people but load only emails which are from Google
  emails: {
    '@loadSeparately': true,
    address: { '@operator': 'LIKE', '@value': '%@gmail.com' }
  }

It results in two SQL queries being executed instead of one which would use a join.

SELECT * FROM person
SELECT * FROM email WHERE address LIKE '%@gmail.com'

It has the effect that the criteria defined for the relationship emails are only applied to that relationship. It will not reduce the found persons if one does not own an email address fitting the given criteria.

Combine multiple criteria

You can also combine multiple criteria objects with one of the logical operators AND, OR or XOR. If you do not state one of these operators between two criteria it will default to OR.

let criteria: Criteria = [
  {
    age: { '@operator': '>=', '@value': 20 },
    emails: {
      '@load': true,
      address: { '@operator': 'LIKE', '@value': '%@icloud.com' }
    }
  },
  'XOR',
  {
    age: { '@operator': '>=', '@value': 30 },
    emails: {
      '@load': true,
      address: { '@operator': 'LIKE', '@value': '%@gmail.com' }
    }
  }
]

Which will create an SQL query like this.

SELECT * FROM person JOIN email ON person.id = email.person_id WHERE (age >= 20 AND email.address LIKE '%@icloud.com') XOR (age >= 30 AND email.address LIKE '%@gmail.com')

Tools

isCriteriaEmpty

Checks if a given criteria object or criteria array does not define any criteria.

import { isCriteriaEmpty } from 'knight-criteria'

isCriteriaEmpty({}) == true
isCriteriaEmpty({ '@not': true }) == true
isCriteriaEmpty({ name: 'Josa' }) == true

isCriteriaComparison

This function checks if a given object implements the the Comparison Interface. It can be useful for more sophisticated algorithms.

import { isCriteriaComparison } from 'knight-criteria'

isCriteriaComparison(1) == false
isCriteriaComparison({}) == false
isCriteriaComparison({ '@value': 1 }) == false
isCriteriaComparison({ '@operator': '!=', '@value': 1 }) == true
isCriteriaComparison({ '@operator': '!=' }) == true

summarizeCriteria

This function will summarize every @-property which is useful if you are dealing with criteria arrays which can contain multiple conflicting @-property definitions.

let criteria = [
  {
    '@limit': 10
  },
  {
    '@limit': 20
  }
]

Here there are two limit definitions which conflict with each other. summarizeCriteria will collect give the first occurence of an @-property the priority and thus creates a standard for dealing with these situations.

import { summarizeCriteria } from 'knight-citeria'

let summarized = summarizeCriteria(criteria)

summarized == {
  '@limit': 10
}

addCriteria

Sometimes you will want to add a certain criterion to given criteria. Since criteria can be in the form of an object or in the form of an array, this function will help you to treat both equally.

In the case of a criteria object it will add the new criterions to that object.

import { addCriteria } from 'knight-criteria'

let criteria = {
  job: 'tree cutter'
}

addCriteria(criteria, {
  age: {
    '@operator': '>=',
    '@value': 30
  }
})

criteria == {
  job: 'tree cutter',
  age: {
    '@operator': '>=',
    '@value': 30
  }
}

In the case of a criteria array it will add the additional criteria to the end of that array connected by an AND operator.

import { addCriteria } from 'knight-criteria'

let criteria = [
  {
    job: 'tree cutter'
  }
]

addCriteria(criteria, {
  age: {
    '@operator': '>=',
    '@value': 30
  }
})

criteria == [
  {
    job: 'tree cutter'
  },
  'AND',
  {
    age: {
      '@operator': '>=',
      '@value': 30
    }
  }
]

workUpCriterion

This function helps you with working up single criterions of the given criteria. This can be used to offer a simplified citeria interface.

Imagine you want to facilitate your interface to access your user database by providing anage criterion which offers to search for users by their age instead of using a birth date.

let critera = {
  age: 30
}

You now would want to work up the criterion and translate it into a birth date interval.

import { CriteriaObject, workUpCriterion } from 'knight-criteria'

workUpCriterion(criteria, 'age', async (criteria: CriteriaObject) => {
  let date = new Date
  date.setFullYear(date.getFullYear() - criteria.age)
  
  criteria.birthDate = {
    '@operator': '<',
    '@value': date
  }

  delete criteria.age
})

This function is especially useful when dealing with criteria arrays which might contain multiple age criterions in different locations.

let criteria = [
  {
    age: 30,
    city: 'Dresden'
  },
  {
    age: 20,
    city: 'Berlin'
  }
]

workUpCriteria

Sometimes you want to process given criteria by adding additional criterions or by altering or deleting existing ones. Since criteria can be in the form of any object or an array which can contain nested array, this function will iterate through the latter ones and will present to you every found criteria object for you to work it up.

import { CriteriaObject, workUpCriteria } from 'knight-criteria'

let criteria = [
  {
    age: 30,
    city: 'Dresden'
  },
  'AND',
  [
    {
      age: 31,
      city: 'Berlin'
    }
  ]
]

workUpCriteria(criteria, (criteriaObject: CriteriaObject) => {
  criteriaObject['job'] = 'tree cutter'
})

criteria == [
  {
    age: 30,
    city: 'Dresden',
    job: 'tree cutter'
  },
  'AND',
  [
    {
      age: 31,
      city: 'Berlin',
      job: 'tree cutter'
    }
  ]
]

transformCriteria

Sometimes you not only want to work up given criteria, sometimes you want to transform them into completely new criteria. This function will iterate through the criteria structure, presenting every criteria object to you which you can use to create a new criteria object which this function will put into the same place of the criteria structure as before, but every part of the structure being different objects.

import { CriteriaObject, transformCriteria } from 'knight-criteria'

let criteria = [
  {
    age: 30,
    city: 'Dresden'
  },
  'AND',
  [
    {
      age: 31,
      city: 'Berlin'
    }
  ]
]

transformCriteria(criteria, (criteriaObject: CriteriaObject) => ({
  group: criteriaObject.age > 30 ? 'Still old' : 'Still young'
}))

criteria == [
  {
    group: 'Still young'
  },
  'AND',
  [
    {
      group: 'Still old'
    }
  ]
]