DEV Community

Cyprien Thao
Cyprien Thao

Posted on

ArrayQuery: ORM-like Querying for JavaScript Arrays

Introducing ArrayQuery: Powerful ORM-like Querying for JavaScript Arrays

Image description

Are you tired of writing complex logic to filter, sort, and paginate arrays in your JavaScript applications? Meet ArrayQuery, a powerful TypeScript library that brings ORM-like querying capabilities to local arrays. With ArrayQuery, you can manipulate and retrieve data from arrays with ease, using a familiar and intuitive API.

Key Features

  • ๐Ÿ“„ Pagination: Easily paginate through large datasets
  • ๐Ÿ”Ž Searching: Perform full-text search across multiple fields
  • ๐Ÿงญ Filtering: Apply complex filters with various match modes
  • ๐Ÿ”ข Sorting: Order results based on any field, including nested properties
  • ๐Ÿ›  Type-safety: Define filters, sort & search with type-safe key paths. Automatic type inference tells you exactly which property can be used

Getting Started

First, install ArrayQuery in your project:

# npm
npm install @chronicstone/array-query

# yarn
yarn add @chronicstone/array-query

# pnpm
pnpm add @chronicstone/array-query

# bun
bun add @chronicstone/array-query
Enter fullscreen mode Exit fullscreen mode

Now, let's dive into some examples to see how ArrayQuery can simplify your data manipulation tasks.

Basic Usage

Here's a simple example that demonstrates filtering, sorting, and pagination:

import { query } from '@chronicstone/array-query'

const users = [
  { id: 1, name: 'John Doe', age: 30, role: 'admin' },
  { id: 2, name: 'Jane Smith', age: 28, role: 'user' },
  { id: 3, name: 'Bob Johnson', age: 35, role: 'user' },
  { id: 4, name: 'Alice Brown', age: 26, role: 'admin' },
  { id: 5, name: 'Charlie Davis', age: 32, role: 'user' },
]

const result = query(users, {
  filter: [
    { key: 'age', matchMode: 'greaterThan', value: 25 },
    { key: 'role', matchMode: 'equals', value: 'user' }
  ],
  sort: { key: 'age', dir: 'desc' },
  page: 1,
  limit: 2
})

console.log(result)
Enter fullscreen mode Exit fullscreen mode

This query will return:

{
  rows: [
    { id: 3, name: 'Bob Johnson', age: 35, role: 'user' },
    { id: 5, name: 'Charlie Davis', age: 32, role: 'user' },
    { id: 2, name: 'Jane Smith', age: 28, role: 'user' }
  ],
  totalPages: 2,
  totalRows: 3
}
Enter fullscreen mode Exit fullscreen mode

In this example, we filtered users older than 25 with the 'user' role, sorted them by age in descending order, and retrieved the first page with 2 items per page.

Searching

ArrayQuery also supports full-text search across multiple fields:

import { query } from '@chronicstone/array-query'

const articles = [
  { id: 1, title: 'Introduction to JavaScript', content: 'JavaScript is a programming language...', author: 'John Doe' },
  { id: 2, title: 'Python for Beginners', content: 'Python is known for its simplicity...', author: 'Jane Smith' },
  { id: 3, title: 'Advanced JavaScript Techniques', content: 'Learn about closures, promises, and async/await...', author: 'Bob Johnson' },
  { id: 4, title: 'Data Science with Python', content: 'Explore data analysis and machine learning...', author: 'Alice Brown' },
]

const result = query(articles, {
  search: {
    value: 'javascript',
    keys: ['title', 'content']
  },
  sort: { key: 'title', dir: 'asc' }
})

console.log(result)
Enter fullscreen mode Exit fullscreen mode

This query will return:

{
  rows: [
    { id: 3, title: 'Advanced JavaScript Techniques', content: 'Learn about closures, promises, and async/await...', author: 'Bob Johnson' },
    { id: 1, title: 'Introduction to JavaScript', content: 'JavaScript is a programming language...', author: 'John Doe' }
  ]
}
Enter fullscreen mode Exit fullscreen mode

This search found all articles containing 'javascript' in either the title or content, and sorted them by title.

The library also gives you control on case sensitivity, either globally or per searched property.

Sorting

ArrayQuery offers powerful sorting capabilities that go beyond simple single-field sorting. Let's explore some advanced sorting features.

Multi-Field Sorting

You can sort your data based on multiple fields, specifying the order for each:

import { query } from '@chronicstone/array-query'

const employees = [
  { id: 1, name: 'Alice', department: 'Sales', salary: 50000 },
  { id: 2, name: 'Bob', department: 'IT', salary: 60000 },
  { id: 3, name: 'Charlie', department: 'Sales', salary: 55000 },
  { id: 4, name: 'David', department: 'IT', salary: 65000 },
  { id: 5, name: 'Eve', department: 'Sales', salary: 52000 },
]

const result = query(employees, {
  sort: [
    { key: 'department', dir: 'asc' },
    { key: 'salary', dir: 'desc' }
  ]
})

console.log(result.rows)
Enter fullscreen mode Exit fullscreen mode

This will sort employees first by department (ascending) and then by salary (descending) within each department:

[
  { id: 4, name: 'David', department: 'IT', salary: 65000 },
  { id: 2, name: 'Bob', department: 'IT', salary: 60000 },
  { id: 3, name: 'Charlie', department: 'Sales', salary: 55000 },
  { id: 5, name: 'Eve', department: 'Sales', salary: 52000 },
  { id: 1, name: 'Alice', department: 'Sales', salary: 50000 }
]
Enter fullscreen mode Exit fullscreen mode

Custom Sorting with Parser

ArrayQuery allows you to use a parser function to transform values before comparison. This is particularly useful when dealing with complex data types or when you need custom sorting logic.

import { query } from '@chronicstone/array-query'

const products = [
  { id: 1, name: 'Laptop', price: '$1200' },
  { id: 2, name: 'Smartphone', price: '$800' },
  { id: 3, name: 'Tablet', price: '$600' },
  { id: 4, name: 'Smartwatch', price: '$300' },
]

const result = query(products, {
  sort: {
    key: 'price',
    dir: 'desc',
    parser: (value) => parseInt(value.replace('$', ''))
  }
})

console.log(result.rows)
Enter fullscreen mode Exit fullscreen mode

This will sort products by price in descending order, correctly parsing the string prices:

[
  { id: 1, name: 'Laptop', price: '$1200' },
  { id: 2, name: 'Smartphone', price: '$800' },
  { id: 3, name: 'Tablet', price: '$600' },
  { id: 4, name: 'Smartwatch', price: '$300' }
]
Enter fullscreen mode Exit fullscreen mode

You can also use built-in parsers for common scenarios:

const result = query(mixedData, {
  sort: { key: 'value', dir: 'asc', parser: 'number' }
})
Enter fullscreen mode Exit fullscreen mode

Available built-in parsers are 'number', 'string', and 'boolean'.

These advanced sorting capabilities allow you to handle complex sorting scenarios with ease, making ArrayQuery a powerful tool for data manipulation in your JavaScript and TypeScript projects.

Filtering

ArrayQuery offers a flexible and powerful filtering system that can handle both simple and complex query scenarios.

Filtering Strategies

ArrayQuery supports two main filtering strategies:

  1. Simple Filters: A list of conditions that are all combined with AND logic.
  2. Filter Groups: Create logical groups of filters, to handle more complex scenarios & granular filtering

Simple Filters

Simple filters are straightforward and easy to use:

import { query } from '@chronicstone/array-query'

const users = [
  { id: 1, name: 'Alice', age: 30, role: 'admin' },
  { id: 2, name: 'Arthur', age: 29, role: 'admin' },
  { id: 3, name: 'Bob', age: 25, role: 'user' },
  { id: 4, name: 'Charlie', age: 35, role: 'user' },
]

const result = query(users, {
  filter: [
    { key: 'age', matchMode: 'greaterThan', value: 25 },
    { key: 'role', matchMode: 'equals', value: 'user' }
  ]
})

console.log(result.rows)
// Output: [{ id: 4, name: 'Charlie', age: 35, role: 'user' }]
Enter fullscreen mode Exit fullscreen mode

Filter Groups

Filter groups allow for more complex logic:

const result = query(users, {
  filter: [
    {
      operator: 'AND',
      filters: [
        { key: 'role', matchMode: 'equals', value: 'admin' },
        { key: 'age', matchMode: 'greaterThanOrEqual', value: '30' },
      ]
    },
    {
      operator: 'AND',
      filters: [
        { key: 'role', matchMode: 'equals', value: 'user' },
        { key: 'age', matchMode: 'lowerThan', value: '30' },
      ]
    }
  ]
})
:
console.log(result.rows)
// Output: [
//   { id: 1, name: 'Alice', age: 30, role: 'admin' },
//   { id: 3, name: 'Bob', age: 25, role: 'user' },
// ]
Enter fullscreen mode Exit fullscreen mode

Available Match Modes

ArrayQuery supports a variety of match modes to cover different filtering needs:

  • equals
  • notEquals
  • contains
  • notContains
  • startsWith
  • endsWith
  • lessThan
  • lessThanOrEqual
  • greaterThan
  • greaterThanOrEqual
  • between
  • exists
  • arrayLength
  • objectMatch

Each match mode is designed for specific types of comparisons. For detailed information on how each match mode works, including examples and edge cases, please refer to our full documentation.

Conclusion

Originally built to make client-side pagination, filtering ,searching & sorting easy to implement on custom DataTable components on front-end apps, Array-Query has a simple & powerful API that you can use for any kind of local array querying.

Online Documentation
GitHub Repository

Top comments (0)