Skip to main content

Built and signed on GitHub Actions

Works with
This package works with Node.js, Deno, BrowsersIt is unknown whether this package works with Cloudflare Workers, Bun
It is unknown whether this package works with Cloudflare Workers
This package works with Node.js
This package works with Deno
It is unknown whether this package works with Bun
This package works with Browsers
JSR Score100%
Downloads1/wk
Published7 months ago (0.4.2)

A sql-like querying and aggregating interface for javascript objects and the DOM

jql

jql is a small library that provides a sql-like interface for querying and aggregating javascript objects and DOM nodes.

Features

  • 🤏 Small size: ~9kb gzipped
  • ☝️ Only one small external dependency
  • 👣 Small footprint: no prototype polluting
  • 🤷 No reason behind it

Usage

Installation

npm i @storynode/jql

Generating documentation

npm run build:docs

DOM querying

import { select } from "@storynode/jql"

const results = await select("tagName", "textContent")
  .from(document)
  .where(
    and(
      tagName("div"),
      hasClass("foo"),
      not(hasClass("bar"))
    )
  )
  .limit(10) // Fetch first 10 results
  .offset(0) // Starting from the first one (the default)
  .run()

Would yield something like this:

[{
  tagName: "DIV",
  textContent: "I am the content of a div"
}, {
  tagName: "DIV",
  textContent: "I am the content of some other div"
}]

Objects / collections

const sampleObj = { name: "obj1", description: "Some object" };

const sampleCollection = [
  { name: "obj23", description: "Object 23" },
  { name: "obj111", description: "My name is Inigo Montoya" },
  { name: "trash", description: "This will not be selected" }
];

const results = await select()
  .from(sampleObj, sampleCollection)
  .where(
      prop("name", /^obj/)
  )
  .run()

Projections and builtin filters support nested properties out of the box:

const sample = [{
    parent: {
      a: 1,
      b: {
        c: 2
      }
    }
  }, {
    some: {
      prop: 1,
      some: {
        prop: 2
      }
    }
  }];

  const result = await select("parent.a").from(sample).run();
  // [{ parent: { a: 1 }}, { parent: { a: null }}]

  const result = await select().from(sample).where(prop("parent.a", 1)).run();
  // [{ parent: { a: 1, b: { c: 2 }}}]

Custom query filters

In the previous examples the provided builtin filters are used for simplicity's sake, but keep in mind that filters are just predicates, namely just functions that take an input and return a boolean, so you can provide any logic you want in those function bodies.

These predicates can be async.

As an extreme example, if you would like to filter all elements based on a flip of a coin:

const sampleObj = { name: "obj1", description: "Some object" };

const sampleCollection = [
  { name: "obj23", description: "Object 23" },
  { name: "obj111", description: "My name is Inigo Montoya" },
  { name: "trash", description: "This will not be selected" }
];

const results = await select()
  .from(sampleObj, sampleCollection)
  .where(
      (el) => Math.random() >= 0.5
  )
  .run()

Data sources

Joining sources

By default, when more than one datasource is specified in a from method call, all datasources are fully joined with one another.

If you want to perform different types of joins you can use one of the provided join operators

For example:

const orders = [{
  orderId: 1,
  userId: 1,
  description: "Foo"
}, {
  orderId: 2,
  userId: 2,
  description: "Bar"
}]

const users = [{
  id: 1,
  userName: "Bob"
}]

const results = await select("user.id", "user.userName", "orders")
  .from(
    innerJoin(users, orders, (user, order) => user.id === order.userId))
  )
  .run()

would yield

[{
  id: 1,
  userName: "Bob",
  orderId: 1,
  description: "Foo"
}]

Mixing datasources

You can mix DOM / collections / objects as datasources in a single query

FAQ

Why, in the name of all that is holy, did you do this?

- Coworkers and friends


Q: Why this library?

A: For absolutely no reason, I just had some time to spare


Q: In what situations is this actually helpful?

A: If you need to combine results from different objects / nodes / collections in a single result set then this is actually useful!


Q: Should I use jql instead of plain old query selectors?

A: It depends: if the filtering you need can be done with document.querySelector[All] and you know how to use it then, by all means, go for it. This can still be usefull if you need to filter results with a function and you want to add some readability to your code.


Q: Does this support joining datasources?

A: Not yet, but it will eventually Yes 😈

Built and signed on
GitHub Actions

Report package

Please provide a reason for reporting this package. We will review your report and take appropriate action.

Please review the JSR usage policy before submitting a report.

Add Package

deno add jsr:@storynode/jql

Import symbol

import * as jql from "@storynode/jql";
or

Import directly with a jsr specifier

import * as jql from "jsr:@storynode/jql";