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

@necrobits/aggregator

v0.2.9

Published

Typescript/Javascript aggregator component

Downloads

101

Readme

Aggregator.js

An flexible aggregator component for your backend (or even frontend).

An implementation for entity source with cache (CachedEntitySource) is also shipped in the library.

You can freely define how to gather your data by implementing your own lookup function.

Install

Using npm

npm install @necrobits/aggregator

or using yarn

yarn add @necrobits/aggregator

Quick start

Given the following data and getters

const users = [
    { id: 'A', name: 'Andy' },
    { id: 'B', name: 'Hai' }
];
const findUsers = ids => users.filter(user => ids.includes(user.id));

const todos = [
    { id: 'T1', task: 'Study' },
    { id: 'T2', task: 'Code' }
];
const findTodos = ids => todos.filter(todo => ids.includes(todo.id));

// You want aggregation for this data
const data = [
    {
        date: '22-02-2022',
        tasks: [
            {assigneeId: 'A', taskId: 'T1'}
            {assigneeId: 'B', taskId: 'T2'}
        ],
    },
// {...},
]

The code for the aggregation looks like this:

const userSource = new SimpleEntitySource('user', {
  lookupUsing: findUsers,
  entityIdBy: 'id',
});

const todoSource = new SimpleEntitySource('todo', {
  lookupUsing: findTodos,
  entityIdBy: 'id',
});
const aggregator = new Aggregator({
  user: userSource,
  todo: todoSource,
});
// or
const aggregator = new Aggregator();
aggregator.register('user', userSource).register('todo', todoSource);

Use the aggregator

const opts = {
  'tasks.*.assigneeId': {
    source: 'user',
    to: {
      key: 'assignee',
    },
    removeIdKey: true,
  },
  'tasks.*.taskId': {
    source: 'todo',
    removeIdKey: true,
  },
};
const aggregatedData = await aggregator.aggregate(data, opts);

console.log(aggregatedData);

Output:

[{
    date: '22-02-2022',
    tasks: [
        {
            id: 'T1',
            name: 'Study'
            assignee: {
                id: 'A',
                name: 'Andy'
            }
        },
        {
            id: 'T2',
            name: 'Code',
            assignee: {
                id: 'B',
                name: 'Hai'
            }
        }
    ]
},
{...}]

Syntax

The syntax to define a aggregation process is as follows:

{
    "<path to the object's ID>": {
        //<Aggregation Options>
    }
}

While declare a path to the object's ID, sometimes you have to access to an array. You can simply use * to tell the aggregator to process every element in that array (see example above).

However, if the data itself is an array, you don't need to use the asterik * at the beginning. The aggregator can recognize that automatically. Meaning, don't write *.userId if you have an array of multiple objects that contains userId,

[{ userId: '1' }, { userId: '2' }];

you can simply use userId directly.

{
    "userId":{
        // options
    }
}

Aggregation Options

| Name | Type | Description | Required | Default | | ------------ | -------------- | ----------------------------------------------------- | -------------------- | --------------------------------- | | source | string | Name of the entity source to gather the data | Yes | | | to | - | If specified, the result is put into another key | | | | to.key | string | The name of the new field to inject the data into | If to is specified | | | to.omitNull | boolean | If the lookuped object is null, remove the key | No | false | | removeIdKey | boolean | Remove the id field after injecting the data | No | false | | transform | (any) => (any) | A function to transform the data before the injection | No | Identity function(v) => v |

Using cache with CachedEntitySource

You can implement an adapter that implements the EntityCache interface to use cache in CachedEntitySource.

Example

This is an example for node-cache. You can also use Typescript if you want to.

export class NodeCacheAdapter<T> implements EntityCache<T> {
  constructor(private nodeCache: NodeCache) {}

  async invalidate(keys: string[]): Promise<void> {
    this.nodeCache.del(keys);
    return;
  }

  async get(key: string): Promise<T> {
    return this.nodeCache.get(key);
  }

  async setBatch(batch: { key: string; value: any }[]): Promise<void> {
    this.nodeCache.mset(
      batch.map((b) => ({
        key: b.key,
        val: b.value,
      }))
    );
  }
}
const cache = new NodeCache();
const userSource = new CachedEntitySource<User>("user",{
    cache: new NodeCacheAdapter<User>(cache);
    lookupUsing: findUsers,
    entityIdBy: "id"
});

CachedEntitySource Options

| Name | Type | Description | | ----------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | cache | EntityCache | The cache instance that implements the EntityCache interface | | lookupUsing | EntityLookupFunction(string[]) => (T[] | Promise<T[]>) | A function that receives an array of IDs and returns an array of entities(or an Promise) | | entityIdBy | string | (T) => string | The name of the ID field in the entity, or a function that receives an entityand returns its ID. |

License

MIT