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

snapjson

v1.2.0

Published

A simple ORM for Node.js using JSON file

Downloads

65

Readme

snapjson

Build Status npm version Size

snapjson is a lightweight NoSQL object-relational mapping (ORM) library for Node.js, written in TypeScript. It allows you to store data in JSON files, with each collection represented as an array of documents.

Table of Contents

Installation

Using npm:

npm install snapjson

Using yarn:

yarn add snapjson

Using pnpm:

pnpm add snapjson

Getting Started

To use the snapjson module, follow these simple steps:

Importing the SnapJson class

import { SnapJson, createCollection } from "snapjson";

Creating an instance of SnapJson

const path = ""; // path of JSON file
const orm = new SnapJson(path);

Defining the data schema

Define your data schema using TypeScript interfaces (the __id property will be added automatically for the primary key):

interface UserSchema {
  name: string;
  email: string;
  password: string;
  age?: number;
}

Creating a collection

// Option 1: Simple collection creation
const collection = await orm.createCollection("user"); // It returns instance of this collection.
console.log(collection); // user

// Option 2: Creating multiple collections at once
const collections = await orm.createCollections(["user", "teacher"]);
console.log(collections); // [ 'user', 'teacher' ]

// Option 3: Creating collection with unique key
const collection = await orm.createCollection({
  collectionName: "user",
  uniqueKeys: ["email"], // This defines all properties that will be unique.
});

Alternatively, you can use a shortcut helper function:

// Instead of orm.createCollection and orm.createCollections, you can use: createCollection function, imported from snapjson.
// createCollection<T>(collection: string | { collectionName: string; uniqueKeys?: Array<keyof T>; }, path?: string, force?: boolean), default path is db/db.json
const collection = await createCollection("user");
const collections = await createCollection(["user", "teacher"]);

Defining a collection

To define a collection, you have three methods available:

Method 1: Using the SnapJson Class

import { SnapJson } from "snapjson";
const path = ""; // default path is db/db.json
const orm = new SnapJson(path);
const usersCollection = await orm.collection<UserSchema>("user");

const usersCollection = await orm.collection<UserSchema>("user", true); // create collection 'user' when it doesn't exist.

This will return an instance of this collection if it exists, otherwise, an error will be thrown.

Method 2: Using shortcut helper function

import { defineCollection } from "snapjson";
const path = ""; // default path is db/db.json
const usersCollection = await defineCollection<UserSchema>("user", path);

const usersCollection = await defineCollection<UserSchema>("user", path, true); // create collection 'user' when it doesn't exist.

Method 3: Using the Collection Class

import { Collection } from "snapjson";
const path = ""; // default path is db/db.json
const usersCollection = new Collection<UserSchema>("user", path);

The key difference between these methods is in their verification timing. The first and second method check if the collection exists before instantiation, while the third method checks upon execution of query methods such as find, findOne, and more.

Removing a collection

const collection = await orm.removeCollection("user");
console.log(collection); // user
const collections = await orm.removeCollection(["teacher", "student"]);
console.log(collections); // [ 'teacher', 'student' ]

// Or
// removeCollection(collections: string | string[], path?: string, force?: boolean), default path is db/db.json
await removeCollection("user");
await removeCollection(["teacher", "student"]);

Query Methods

Use collection methods for querying:

Inserting documents

const usersCollection = await orm.collection<UserSchema>("user");

// Or

const usersCollection = await defineCollection<UserSchema>("user");

// Inserting one document
const alice = await usersCollection.insertOne({
  email: "[email protected]",
  name: "Alice",
  password: "!13x47dh32",
  age: 25,
});

console.log(alice.toObject());

/*
  {
    email: '[email protected]',
    name: 'Alice',
    password: '!13x47dh32',
    age: 25,
    __id: 1
  }
*/

// Inserting multiple documents at once
const users = await usersCollection.insertMany([
  {
    email: "[email protected]",
    name: "Carole",
    password: "!13x9dnsnv",
    age: 22,
  },
  {
    email: "[email protected]",
    name: "Mary",
    password: "xnjsd7432&8",
    age: 20,
  },
]);

users.forEach((user) => {
  console.log(user.toObject());
});

/*
  {
    email: '[email protected]',
    name: 'Carole',
    password: '!13x9dnsnv',
    age: 22,
    __id: 2
  }
  {
    email: '[email protected]',
    name: 'Mary',
    password: 'xnjsd7432&8',
    age: 20,
    __id: 3
  }
*/

Finding documents

// Finding one document
const carole = await usersCollection.findOne(
  { name: "Carole" },
  { select: ["__id", "age", "email", "name"] }
);
if (carole) console.log(carole.toObject());

/*
  {
    email: '[email protected]',
    name: 'Carole',
    age: 22,
    __id: 2
  }
*/

// Finding documents
const users = await usersCollection.find(
  { age: { $gte: 20 } },
  { select: ["__id", "age", "email", "name"], limit: 3 }
);
users.forEach((user) => {
  console.log(user.toObject());
});

/*
  { __id: 1, age: 25, email: '[email protected]', name: 'Alice' }
  { __id: 2, age: 22, email: '[email protected]', name: 'Carole' }
  { __id: 3, age: 20, email: '[email protected]', name: 'Mary' }
*/

Updating documents

/*
  Before updating:

  {
    email: '[email protected]',
    name: 'Mary',
    password: 'xnjsd7432&8',
    age: 20,
    __id: 3
  }
*/
const data = { age: 21 };
const user = await usersCollection.updateOne(data, { __id: 3 });
if (user) console.log(user.toObject());

/*
  After updating:

  {
    email: '[email protected]',
    name: 'Mary',
    password: 'xnjsd7432&8',
    age: 21, ✔
    __id: 3
  }
*/

Deleting documents

const user = await usersCollection.deleteOne({ __id: 3 });
if (user) console.log(user.toObject());

/*
  {
    email: '[email protected]',
    name: 'Mary',
    password: 'xnjsd7432&8',
    age: 21,
    __id: 3
  }
*/

Document methods

const user = await usersCollection.findById(1);

if (user) {
  console.log(user.toObject()); // Convert document into a plain object.
  console.log(user.toJSON()); // Convert document into JSON.
  user.age = 10;
  await user.save(); // Save the changes to the document
  await user.delete(); // Delete the document from the collection
}

Additional methods

SnapJson class

/*
  - getCollections
  - isExistCollection
  - size
  - pathDB
*/

const collections = await orm.getCollections(); // Returns all collection names available in the database.
console.log(collections); // [ 'user' ]

const isExists = await orm.isExistCollection("user"); // Returns true if the collection exists in the database, otherwise returns false.
console.log(isExists); // true

const databaseSize = await orm.size(); // Returns the size of the database file.
console.log(databaseSize); // 58 KB

console.log(orm.pathDB); // db/db.json

Collection class

/*
  - addUniqueKey
  - getUniqueKeys
  - removeUniqueKey
  - removeUniqueKey
  - removeAllUniqueKeys
  - count
  - size
  - lastInsertId
  - add
  - create
*/

// addUniqueKey
const key = await usersCollection.addUniqueKey("email");
console.log(key); // email

const keys = await usersCollection.addUniqueKey(["email", "name"]);
console.log(key); // [ 'email', 'name' ]

// getUniqueKeys
const keys = await usersCollection.getUniqueKeys();
console.log(keys); // [ 'email', 'name' ]

// removeUniqueKey
const keys = await usersCollection.removeUniqueKey("name");
console.log(keys); // name

// removeAllUniqueKeys
const keys = await usersCollection.removeAllUniqueKeys();
console.log(keys); // [ 'email', 'name' ]

// count
const ct = await usersCollection.count();
console.log(ct); // 3
// Or
await usersCollection.count({ age: { $lte: 30 } });

// size
const collectionSize = await usersCollection.size();
console.log(collectionSize); // 26 KB

// lastInsertId
const id = await usersCollection.lastInsertId();
console.log(id); // 3

// add and create methods are aliases for insertOne method

Operators

Operators are special symbols or keywords that allow you to carry out mathematical or logical operations. snapjson provides a large number of operators to help you build complex queries.

snapjson offers the following query operator types:

  • Comparison
  • Logical
  • Array

Comparison Operators

snapjson comparison operators can be used to compare values in a document. The following table contains the common comparison operators.

| Operators | Description | | --------- | --------------------------------------------------------------- | | $eq | Matches values that are equal to the given value. | | $ne | Matches values that are not equal to the given value. | | $gt | Matches if values are greater than the given value. | | $gte | Matches if values are greater than or equal to the given value. | | $lt | Matches if values are less than the given value. | | $lte | Matches if values are less than or equal to the given value. | | $in | Matches any of the values in an array. | | $nin | Matches none of the values specified in an array. |

$eq Operator

In this example, we retrieve the document with the exact id value 2.

const user = await usersCollection.findOne({ __id: { $eq: 2 } });
if (user) console.log(user.toObject());

/*
  {
    email: '[email protected]',
    name: 'Carole',
    password: '!13x9dnsnv',
    age: 22,
    __id: 2
}

$ne Operator

Suppose we have a collection of students, and we need to find all documents whose age field is not equal to 40:

await studentsCollection.find({ age: { $ne: 40 } }))

/*
  [
    {
      __id: 1,
      name: "Gonzalez Estrada",
      gender: "male",
      age: 39,
      email: "[email protected]",
    },
    {
      __id: 2,
      name: "Burris Leon",
      gender: "male",
      age: 37,
      email: "[email protected]",
    },
    {
      __id: 3,
      name: "Maryanne Wagner",
      gender: "female",
      age: 32,
      email: "[email protected]",
    },
  ];
*/

$gt Operator

In this example, we retrieve the documents where the age field is greater than 35:

await studentsCollection.find({ age: { $gt: 35 } });

/*
  [
    {
      __id: 1,
      name: "Gonzalez Estrada",
      gender: "male",
      age: 39,
      email: "[email protected]",
    },
    {
      __id: 2,
      name: "Burris Leon",
      gender: "male",
      age: 37,
      email: "[email protected]",
    },
    {
      __id: 6,
      name: "Alyce Strickland",
      gender: "female",
      age: 40,
      email: "[email protected]",
    },
  ];
*/

$lt Operator

Let’s find the documents whose age field is less than 35:

await studentsCollection.find({ age: { $lt: 35 } });

/*
  [
    {
      __id: 3,
      name: "Maryanne Wagner",
      gender: "female",
      age: 32,
      email: "[email protected]",
    },
    {
      __id: 4,
      name: "Fay Fowler",
      gender: "female",
      age: 24,
      email: "[email protected]",
    },
    {
      __id: 5,
      name: "Angeline Conner",
      gender: "female",
      age: 20,
      email: "[email protected]",
    },
  ];
*/

$gte Operator

Suppose we have a collection of students, and we need to find all documents whose age field is greater than or equal to 35.

await studentsCollection.find({ age: { $gte: 35 } });

/*
  [
    {
      __id: 24,
      name: "Linda Henderson",
      gender: "female",
      age: 35, ✔
      email: "[email protected]",
    },
    {
      __id: 26,
      name: "Beryl Howe",
      gender: "female",
      age: 40,
      email: "[email protected]",
    },
    {
      __id: 28,
      name: "Russell Mcclure",
      gender: "male",
      age: 39,
      email: "[email protected]",
    },
  ];
*/

$lte Operator

Let’s find the documents whose age field is less than or equal to 35.

await studentsCollection.find({ age: { $lte: 35 } });

/*
  [
    {
      __id: 29,
      name: "Mcmahon Wilkinson",
      gender: "male",
      age: 23,
      email: "[email protected]",
    },
    {
      __id: 30,
      name: "Nita Knapp",
      gender: "female",
      age: 35, ✔
      email: "[email protected]",
    },
    {
      __id: 31,
      name: "Sampson Morrison",
      gender: "male",
      age: 26,
      email: "[email protected]",
    },
  ];
*/

$in Operator

The following query returns documents where the age field contains the given values.

await studentsCollection.find({ age: { $in: [20, 30, 40] } });

/*
  [
    {
      __id: 70,
      name: "Hilary Watson",
      gender: "female",
      age: 20,
      email: "[email protected]",
    },
    {
      __id: 82,
      name: "Estrada Ramsey",
      gender: "male",
      age: 30,
      email: "[email protected]",
    },
    {
      __id: 91,
      name: "Lela Howard",
      gender: "female",
      age: 40,
      email: "[email protected]",
    },
  ];
*/

$nin Operator

In this example, we retrieve the documents where the age field doesn't contain the given values.

await studentsCollection.find({ age: { $nin: [20, 30, 40] } });

/*
 [
    {
      __id: 1,
      name: "Gonzalez Estrada",
      gender: "male",
      age: 39,
      email: "[email protected]",
    },
    {
      __id: 2,
      name: "Burris Leon",
      gender: "male",
      age: 37,
      email: "[email protected]",
    },
    {
      __id: 3,
      name: "Maryanne Wagner",
      gender: "female",
      age: 32,
      email: "[email protected]",
    },
  ];
*/

Logical Operators

Logical operators are used to filter data based on given conditions. They provide a way to combine multiple conditions.

snapjson provides two logical operators: $or and $and.

| Operator | Description | | -------- | ----------------------------------------------------------------------------------------------------- | | $and | Joins two or more queries with a logical AND and returns the documents that match all the conditions. | | $or | Join two or more queries with a logical OR and return the documents that match either query. |

$and Operator

Find documents that match both the following conditions:

◻ gender is equal to "male" ◻ age is between 20 and 25

await studentsCollection.find({
  $and: [
    { gender: "male" },
    { age: { $gte: 20, $lte: 25 } },
  ]
})

/*
  [
    {
      __id: 13,
      name: "Martinez Potts",
      gender: "male",
      age: 20,
      email: "[email protected]",
    },
    {
      __id: 22,
      name: "Patton Molina",
      gender: "male",
      age: 22,
      email: "[email protected]",
    },
    {
      __id: 27,
      name: "Key Mercado",
      gender: "male",
      age: 25,
      email: "[email protected]",
    },
  ];
/*

$or Operator

Find documents that match either of the following conditions:

◻ gender is equal to "male" or "female"

await studentsCollection.find({
  $or: [{ gender: "male" }, { gender: "female" }],
});

/*
  [
    {
      __id: 1,
      name: "Gonzalez Estrada",
      gender: "male",
      age: 39,
      email: "[email protected]",
    },
    {
      __id: 2,
      name: "Burris Leon",
      gender: "male",
      age: 37,
      email: "[email protected]",
    },
    {
      __id: 3,
      name: "Maryanne Wagner",
      gender: "female",
      age: 32,
      email: "[email protected]",
    },
  ];
*/

Array Operators

snapjson provides several operators for searching arrays. Here are the array operators provided by snapjson.

| Operator | Description | | ----------- | ------------------------------------------------------------------------------------------ | | $eq | Matches arrays that equal to the specified array. e.g. [1, 2, 3] == [1, 2, 3] | | $ne | Matches arrays that do not equal to the specified array. e.g. [1, 2, 3] !== [3, 2, 1] | | $contains | Matches arrays that contain all the specified values. | | $nocontains | Matches arrays that do not contain all the specified values. |

Basic query

Suppose we have a collection of shoes, and we need to find all shoes that have 3 colors: the first color is red, second is white, and third is green.

await shoesCollection.find({ colors: ["red", "white", "green"] });

/*
  [
    {
      __id: 231,
      price: '$88',
      colors: [ 'red', 'white', 'green' ],
      rate: 4,
      nb_view: 836,
      madeIn: 'Peru'
    }
  ]
*/

// Note that arr1 and arr2 are not equal; on the other hand, arr3 and arr4 are equal.

const arr1 = [1, 2, 3];
const arr2 = [3, 2, 1];

const arr3 = [1, [2], 3];
const arr4 = [1, [2], 3];

$eq and $ne operators

We have already explained in detail their use cases. They are used in the same way for the arrays.

$contains operator

Suppose we have a collection of shoes, and we need to find all shoes that have at least 2 colors: white and black.

await shoesCollection.find({ colors: { $contains: ["black", "white"] } });

/*
  [
    {
      __id: 361,
      price: "$48",
      colors: ["blue", "black", "white"],
      rate: 0,
      nb_view: 163,
      madeIn: "Spain",
    },
    {
      __id: 411,
      price: "$50",
      colors: ["black", "green", "white"],
      rate: 4,
      nb_view: 100,
      madeIn: "Korea (South)",
    },
    {
      __id: 611,
      price: "$79",
      colors: ["white", "black"],
      rate: 4,
      nb_view: 784,
      madeIn: "Comoros",
    },
  ];
*/

$nocontains operator

In this example, we retrieve the documents where the colors field doesn't contain white and black colors at once..

await shoesCollection.find({ colors: { $nocontains: ["black", "white"] } });

/*
  [
    {
      __id: 971,
      price: "$54",
      colors: ["blue", "brown", "green"],
      rate: 0,
      nb_view: 692,
      madeIn: "Indonesia",
    },
    {
      __id: 981,
      price: "$44",
      colors: ["yellow", "white"], => Here, white color is alone.
      rate: 4,
      nb_view: 632,
      madeIn: "Malawi",
    },
    {
      __id: 991,
      price: "$45",
      colors: ["cyan", "purple", "red"],
      rate: 4,
      nb_view: 955,
      madeIn: "Malta",
    },
  ];
*/

await shoesCollection.find({ colors: { $nocontains: "white" } });

/*
  [
    {
      __id: 971,
      price: "$54",
      colors: ["blue", "brown", "green"],
      rate: 0,
      nb_view: 692,
      madeIn: "Indonesia",
    },
    {
      __id: 991,
      price: "$45",
      colors: ["cyan", "purple", "red"],
      rate: 4,
      nb_view: 955,
      madeIn: "Malta",
    },
  ];
*/

Working with Regular Expressions

snapjson supports regular expressions for string-based queries. You can use regular expressions with various operators to search for patterns within string.

await studentsCollection.find({ email: /.com$/ });
// $eq
await studentsCollection.find({ email: { $eq: /.com$/ } });
// $ne
await studentsCollection.find({ email: { $ne: /.com$/ } });
// $in
await studentsCollection.find({ email: { $in: [/.com$/, /.org$/] } });
// $nin
await studentsCollection.find({ email: { $nin: [/.com$/, /.org$/] } });
// $contains
await shoesCollection.find({ colors: { $contains: [/re/, "cyan"] } });

/*
  [
    {
      __id: 631,
      price: "$91",
      colors: ["white", "green", "cyan"],
      rate: 0,
      nb_view: 817,
      madeIn: "Armenia",
    },
    {
      __id: 721,
      price: "$11",
      colors: ["red", "blue", "cyan"],
      rate: 1,
      nb_view: 849,
      madeIn: "Moldova",
    },
    {
      __id: 991,
      price: "$45",
      colors: ["cyan", "green", "red"],
      rate: 4,
      nb_view: 955,
      madeIn: "Malta",
    },
  ];
*/

Reporting Issues

If you encounter any issues, have questions, or want to contribute to the project, please visit the GitHub repository and open an issue.

License

This project is licensed under the MIT License.