Create a self-maintaining index via proxy

Overview

proxy-indexer

Proxy-indexer allows you to easily index collections of mutable objects based on their own mutable properties.

While this is relatively easy to implement for immutable properties, it is challenging to keep an index up to date when it is based on a mutable property. Consider, you want to look up a set of orders based on their order status. However, their order status frequently changes. Proxy-indexer makes this process transparent via the following API:

import { createHashIndex } from 'proxy-indexer';

const [{ statusIndex: orderStatusIndex }, captureOrder] = createHashIndex({
  targetProperties: ['status'],
});

const exampleOrder = captureOrder({
  orderId: '123abc',
  status: 'PLACED',
});

const placedOrders = orderStatusIndex.get('PLACED');
console.log(exampleOrder === placedOrders.values().next().value); // true

exampleOrder.status = 'SHIPPED';

console.log(placedOrders.size); // 0

const shippedOrders = orderStatusIndex.get('SHIPPED');
console.log(exampleOrder === shippedOrders.values().next().value); // true

In the above example you will notice that the size of the placedOrders set reduced from 1 to 0 as soon as we updated the target property (status) of our example order.

When you have thousands of objects and need to look them up by a particular property, this indexing method is more than 1000x times faster than the alternative -- iterating and filtering.

const [{ statusIndex: orderStatusIndex }, captureOrder] = createHashIndex({
  targetProperties: ['status'],
});

const statusMap = { 0: 'PLACED', 1: 'SHIPPED', 2: 'DELIVERED', 3: 'CANCELLED'};
const allObjects = Array.from(
  { length: 125000 }, 
  (_, i) => captureOrder({id: i, status: statusMap[i % 4] })
);

// Let's find all shipped orders the old fashioned way
const shippedOld = Object.values(allObjects).filter(({status}) => status === 'SHIPPED');
// ~4ms

// And the new way
const shipped = orderStatusIndex.get('SHIPPED');
// ~0.002ms

Not only is the new way much faster, but shipped will always be up-to-date, while shippedOld will contain incorrect records once a shipped order is updated to 'DELIVERED' -- the filtered view has to be recalculated each time it is used.

The following test run was performed on a Macbook Air with an M2 CPU and 16GB RAM:

  README.md examples
    ✔ returns indexed objects
    ✔ updates indexes when values change
    Performance tests
Vanilla lookup: 4.186499834060669ms
Indexed lookup: 0.0023751258850097656ms
Index lookups are 1763 times faster
      ✔ looks up objects more than 1000x faster
Vanilla update: 0.0009169578552246094ms
Indexed update: 0.001291036605834961ms
Updates are 1.41 times slower
      ✔ updates are not more than twice as slow


  4 passing (9ms)

Basic usage

Using an index has two steps:

  • Instantiating the index with a list of indexed properties
  • Giving it control of an object you want to index via the captureObject command returned in the tuple following instantiation

Let's examine the first step now:

import { createHashIndex } from 'proxy-indexer';

type Customer = {
  name: string;
  country: 'US' | 'AU' | 'NZ' | 'UK' | 'FR';
  status: 'ACTIVE' | 'PENDING' | 'BANNED' | 'DEACTIVATED';
}

const [
  { statusIndex, countryIndex }, 
  captureCustomer
] = createHashIndex<Customer>({
  targetProperties: ['status', 'country'],
});

In the above example we instantiate an index for our customer entity, with indexes on country and status. The createHashIndex function returns a tuple containing:

  • An object containing the indexes, each as a property keyed by '${targetProperty}Index'
  • A capture function

Using the capture function correctly is critical to proxy-indexer usage. The capture function takes an object and returns a proxied version of the same object. This allows the index to trap or "spy on" property setting operations. This means that for proxy-indexer to work, all subsequent calls to the indexed object must be via the proxy, not the original object.

The best way to ensure there is no access to the original object is to capture it at instantiation:

const createCustomer = (name: string, country: 'US' | 'AU' | 'NZ' | 'UK' | 'FR'): Customer => {
  return captureCustomer({
    name,
    country,
    status: 'PENDING'
  });
}
You might also like...

Simple webpack plugin that generates VERSION and commitInfo insert index.html during build

Html commit version plugin Simple webpack plugin that generates CommitInfo and VERSION insert index.html during build. English | 简体中文 Usage Given a we

Mar 8, 2022

Index your Google Drive Easily and Free.

Index your Google Drive Easily and Free.

Google Personal/Shared Drive Index Full White label and Customizable Index | One of a kind Supports Both My and Team/Shared Drives with Dark Mode. Cli

Sep 22, 2021

A personal semantic search engine capable of surfacing relevant bookmarks, journal entries, notes, blogs, contacts, and more, built on an efficient document embedding algorithm and Monocle's personal search index.

A personal semantic search engine capable of surfacing relevant bookmarks, journal entries, notes, blogs, contacts, and more, built on an efficient document embedding algorithm and Monocle's personal search index.

Revery 🦅 Revery is a semantic search engine that operates on my Monocle search index. While Revery lets me search through the same database of tens o

Dec 30, 2022

Experimental URL-CID index using b trees (chunky-trees from @mikeal)

ipfs-url-index Experimental IPFS index for URL-CID, implemented using chunky-trees B-Tree implementation. API Server Run node main.js to start the ap

Mar 14, 2022

Вlockchain feed index Google Colab

blockchain-feed-index Run Google Colab https://colab.research.google.com/drive/1OShIMVcFZ_khsUIBOIV1lzrqAGo1gfm_?usp=sharing Installation Install node

Jun 25, 2022

Useful for managing a wrapping index for an array of items.

use-wrapping-index Useful for managing a wrapping index for an array of items. Usage import useWrappingIndex from "@alexcarpenter/use-wrapping-index";

Dec 9, 2022

In this project, I built a simple HTML list of To-Do tasks. This simple web page was built using Webpack, creating everything from a JavaScript index file that imported all the modules and assets

In this project, I built a simple HTML list of To-Do tasks. This simple web page was built using Webpack, creating everything from a JavaScript index file that imported all the modules and assets

To Do List In this project, I built a simple HTML list of To-Do tasks. This simple web page was built using Webpack, creating everything from a JavaSc

Mar 31, 2022

I'm trying to create simple program for adding the digital signature to a pdf file with self-signed certificate. I use node-signpdf and pdf-lib library.

pdf-digital-signature-with-node-signpdf-ejs I'm trying to create simple program for adding the digital signature to a pdf file with self-signed certif

Dec 25, 2022

A simple script to create Gaia-X Self Descriptions

How To Use Update the self description in self-description.json. Create a new .env file with PRIVATE_KEY, CERTIFICATE, VERIFICATION_METHOD and X5U_URL

Nov 21, 2022
Owner
Anthony Manning-Franklin
Anthony Manning-Franklin
Digital Identifier is a secure, decentralized, anonymous and tampered proof way of maintaining and verifying all essential identity-based documents to create a unique digital identity of a person.

Digital Identifier ?? To design and develop a secure, decentralized, anonymous and tampered proof way of maintaining and verifying all essential ident

Mukul Kolpe 4 Dec 17, 2022
proxy 🦄 yxorp is your Web Proxy as a Service (SAAS) Multi-tenant, Multi-Threaded, with Cache & Article Spinner

proxy ?? yxorp is your Web Proxy as a Service (SAAS) Multi-tenant, Multi-Threaded, with Cache & Article Spinner. Batteries are included, Content Spinning and Caching Engine, all housed within a stunning web GUI. A unique high-performance, plug-and-play, multi-threaded website mirror and article spinner

4D/ҵ.com Dashboards 13 Dec 30, 2022
Proxy but misspelled -- closed proxy for the internet

pyrox Proxy that runs on Cloudflare Workers. Setup Install wrangler2. npm install wrangler. Generate a public Ed25519 key, exported under SPKI mode wi

bots.gg 10 Sep 9, 2022
This is a library to alternate and self-host the Prisma Data Proxy (cloud.prisma.io)

Alternative Prisma Data Proxy This is a library to alternate and self-host the Prisma Data Proxy (cloud.prisma.io). In order to deploy your project to

AijiUejima 60 Dec 28, 2022
💀 A bare-minimum solution to solve CORS problem via proxy API

?? CORS Hijacker A bare-minimum solution to solve CORS problem via proxy API Base URL https://cors-hijacker.vercel.app/ Get Get Basic HTML Endpoint: $

Irfan Maulana 35 Nov 4, 2022
Obsidian plugin that allows user to create a glossary of files, an index of files or both.

Obsidian Auto Glossary Auto Glossary is an Obsidian plugin to create a glossary, an index or a glossary with an index from the files you want. Feature

Ennio Italiano 29 Dec 30, 2022
Resolve parallel promises in key-value pairs whilst maintaining type information

async-kv Resolves promises in key-value pairs maintaining type information. Prerequisites NodeJS 12 or later Installation npm i async-kv yarn add asyn

Tony Tamplin 4 Feb 17, 2022
A simple HTML page maintaining list of To Do tasks using webpack and served by a webpack dev server.

Task Organiser In this project, we build a simple HTML list of To Do tasks using webpack and served by a webpack dev server. Built With HTML CSS Javas

Angom 4 Jun 24, 2022
Github Actions and Workflows that make maintaining Magento2 projects and modules easier.

Magento 2 GitHub Actions Opinionated Github Actions and Workflows to make building, testing, and maintaining Magento 2 Modules easier. README if you a

Graycore, LLC 35 Dec 21, 2022