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-object-db

v1.0.1

Published

An in-memory database for objects

Downloads

1

Readme

Knight Object DB by Coderitter

An in-memory database for objects. It is a really nice fit for browsers that want to store the user data locally to offer offline capabilities. Changed entities can be updated or removed and the database can be queried.

Related packages

This package uses knight-change to return a list of changes the database undertook while integrating or removing objects. Furthermore it uses knight-criteria to offer an interface for querying the database for objects.

You can combine this package with knight-event-bus to distribute changes to UI components for example of a React app. The components would listen to changes and if one is relevant for what they are displaying they would rerender accordingly. This way you can have a local data storage which can be updated from the server and every time something changes the components can rerender. You can realize contineous UI updates and offline capabilities with this.

Install

npm install knight-change

Overview

Define domain objects

class Knight {
    id?: number
    name?: string
    bestFriendId?: number
  
    bestFriend?: Knight
    knightsWhoThinkIAmTheirBestFriend?: Knight[]
    address?: Address
}

class Address {
    knightId?: number
    street?: string

    knight?: Knight
}

Define a schema which is used to wire the relationships

import { Schema } from 'knight-object-db'

let schema = {
    // the keys in the root schema object are names of tables (be aware of casing!)
    'knight': {
        // you need to define the id properties
        // this information will be used to determine if two objects are the same
        idProps: ['id'],

        relationships: {
            // here a many-to-one and one-to-many relationships
            // the keys are property names on the object

            // many-to-one
            'bestFriend': {
                manyToOne: true,
                thisId: 'bestFriendId',
                otherEntity: 'knight',
                otherId: 'id'
            },

            // one-to-many
            'knightsWhoThinkIAmTheirBestFriend': {
                oneToMany: true,
                thisId: 'id',
                otherEntity: 'knight',
                otherId: 'bestFriendId'
            },

            // one-to-one
            'address': {
                manyToOne: true,
                thisId: 'id',
                otherEntity: 'address',
                otherId: 'knightId'
            }
        },
    },

    'address': {
        columns: {
            'knight_id': 'knight_id',
            'street': 'street'
        },
        relationships: {
            'knight': {
                manyToOne: true,
                thisId: 'knight_id',
                otherEntity: 'knight',
                otherId: 'id',
            }
        }
    } as Schema
}

Initializing the database

let db = new ObjectDb(schema)

Integrate objects into the database

Store a plain JavaScript object into the database. You will need to give the class either as string or as class function.

let obj = {
    id: 1
    name: 'garz'
}

db.integrate('Knight', obj)
db.integrate(Knight, obj)

// the object is stored inside the property objects
db.objects == {
    'Knight': [ obj ]
}

// storing the object without a class name will store it as Object in the database
db.integrate(obj)

db.objects == {
    'Object': [ obj ]
}

Or you use an instance of a class.

let knight = new Knight(1, 'garz')

db.integrate(knight)

// the instance of class Knight is stored inside the property objects
db.objects == {
    'Knight': [ knight ]
}

You can also give an array of objects.

db.integrate([ new Knight(1), new Knight(2) ])
db.integrate(Knight, [{ id: 1 }, { id: 2 }])
db.integrate('Knight', [{ id: 1 }, { id: 2 }])

You can update an object by feeding another object which needs to have the same id as the object you wish to update. The object that is already inside the database will be updated and remain in place.

let knight = new Knight(1, 'garz')
db.integrate(knight)

let updated = new Knight(1, 'René')
db.integrate(updated)

db.objects == {
    'Knight': [ knight ] // knight object still in place after updating
}

If the object comes with set relationships, the database will store them in their own array, while leaving the relationships in place. That means that the objects inside the database are still connected to each other.

let garz = new Knight(1, 'garz')
garz.bestFriendId = 2

garzAddress = new Address('Jordan Street')
garzAddress.knightId = 1

let none = new Knight(1, 'none')

garz.address = garzAddress // assigned
garz.bestFriend = none // assigned
none.knightsWhoThinkIAmTheirBestFriend = [ garz ] // assigned
garzAddress.knight = garz // assigned

db.integrate(garz)

db.object = {
    'Knight': [ garz, none ],
    'Address': [ garzAddress ]
}

You can also integrate objects that do not have their relationships set. The database will wire them according to the schema.

let garz = new Knight(1, 'garz')
garz.bestFriendId = 2 // best friend id is set

let garzAddress = new Address('Jordan Street')
garzAddress.knightId = 1 // knight id is set

let none = new Knight(2, 'none')

garz.bestFriend = undefined // unassigned
garz.address = undefined // unassigned
none.knightsWhoThinkIAmTheirBestFriend = undefined // unassigned
garzAddress.knight = undefined // unassigned

db.integrate(garz)
db.integrate(none)
db.integrate(garzAddress)

// after integrating the objects into the database they got fully wired

garz.bestFriend == none
garz.address == garzAddress
none.knightsWhoThinkIAmTheirBestFriend == [ garz ]
garzAddress.knight = garz

The database will return a Changes object, containing all the changes that were made to the database while integrating the given object.

/* create an object */
let garz = new Knight(1, 'garz')
let changes = db.integrate(garz)

changes.changes == [
    {
        entityName: 'Knight',
        entity: garz, // the object which is stored inside the database
        method: { method: 'create' }
    }
]

/* update an object */
let updated = new Knight(1, 'René')
let changes = db.integrate(updated)

changes.changes == [
    {
        entityName: 'Knight',
        entity: garz, // the object which was already in the database
        method: { method: 'method', props: ['name'] }
    }
]

Remove objects from the database

Removing an object will also unwire its relationships.

let garz = new Knight(1, 'garz')
garz.bestFriendId = 2

garzAddress = new Address('Jordan Street')
garzAddress.knightId = 1

let none = new Knight(1, 'none')

garz.bestFriend = none // assigned
garz.address = garzAddress // assigned
none.knightsWhoThinkIAmTheirBestFriend = [ garz ] // assigned
garzAddress.knight = garz // assigned

db.integrate(garz)

let changes = db.remove(Knight, { id: 1 })

changes.changes == [
    {
        entityName: 'Knight',
        entity: garz,
        method: { method: 'delete' }
    }
]

// the database after removing
db.objects = {
    'Knight': [ none ],
    'Address': [ garzAddress ]
}

// the relationships after removing
garz.bestFriend = null
none.knightsWhoThinkIAmTheirBestFriend = []
garzAddress.knight == null

// but the ids are still there
garz.bestFriendId = 2
garzAddress.knightId = 1

You can also remove an object tree at once.

let obj = {
    id: 1,
    bestFriend: {
        id: 2
    },
    address: {
        knightId: 1
    }
}

let changes = db.remove(Knight, obj)

changes.changes == [
    {
        entityName: 'Knight',
        entity: garz,
        method: { method: 'delete' }
    },
    {
        entityName: 'Knight',
        entity: none,
        method: { method: 'delete' }
    },
    {
        entityName: 'Address',
        entity: garzAddress,
        method: { method: 'delete' }
    }
]

Or an array of objects.

db.remove([ new Knight(1), new Knight(2) ])
db.remove(Knight, [{ id: 1 }, { id: 2 }])
db.remove('Knight', [{ id: 1 }, { id: 2 }])

Querying the database

The database implements the a querying API on the basis of the Criteria interface which is part of knight-criteria package.

/* returns all knights with the id 1 */
db.read(Knight, { id: 1 })
db.read('Knight', { id: 1 })

/* returns all knights which name starts with 'g' */
db.read(Knight, { name: { operator: 'LIKE', value: 'g%' }})
db.read('Knight', { name: { operator: 'LIKE', value: 'g%' }})

The returned objects are fully wired and ready to use. The database also ensures that the received objects will stay the same throughout the lifetime of the database.