Skip to content

tasteee/listerine-db

Repository files navigation

listerineDB

listerineDB is a lightweight, powerful, persistent data store for Node-based applications. It provides a powerful querying system with MongoDB-like syntax for inserting, updating, querying, and removing documents with automatic persistence to the file system.

Features

  • 📁 File-based storage with automatic JSON persistence
  • 🔍 Powerful querying using the listerine library
  • ⏰ Automatic timestamp management (createdDate, updatedDate)
  • 🆔 UUID generation for document IDs
  • 📊 Collection statistics and metadata
  • 🔄 Bulk operations support
  • 🛡️ Error handling and validation

Installation

npm install listerine-db

Quick Start

import { listerineDB, BaseDocumentT } from './listerine-db'

// Define your document type
interface User extends BaseDocumentT {
  name: string
  email: string
  age: number
}

// Create a collection
const users = listerineDB.createCollection<User>({
  filePath: './data/users.json',
  defaultData: [],
})

// Insert a document
const result = users.insert({
  name: 'John Doe',
  email: 'john@example.com',
  age: 30,
})

// Query documents
const allUsers = users.find()
const johnDoe = users.findOne({ name: 'John Doe' })

API Reference

BaseDocumentT

All documents must extend BaseDocumentT:

interface BaseDocumentT {
  id: string // Unique identifier (auto-generated UUID)
  createdDate?: number // Timestamp when document was created
  updatedDate?: number // Timestamp when document was last updated
}

Collection Class

Constructor Options

interface CollectionOptionsT<DataT extends BaseDocumentT> {
  filePath: string // Path to the JSON file for persistence
  defaultData?: DataT[] // Default documents to create if file doesn't exist
}

Query Methods (Read-only)

These methods don't trigger file writes and are safe for frequent use:

find(query?: any): DataT[]

Returns all documents matching the query. If no query is provided, returns all documents.

// Get all documents
const allUsers = users.find()

// Query with conditions
const adults = users.find({ age: { $gte: 18 } })
const johnDoe = users.find({ name: 'John Doe' })
findById(id: string): DataT | undefined

Finds a single document by its ID.

const user = users.findById('123e4567-e89b-12d3-a456-426614174000')
findByIds(ids: string[]): DataT[]

Finds multiple documents by their IDs.

const selectedUsers = users.findByIds(['id1', 'id2', 'id3'])
findOne(query?: any): DataT | undefined

Returns the first document matching the query.

const firstAdmin = users.findOne({ role: 'admin' })
count(query?: any): number

Returns the number of documents matching the query.

const totalUsers = users.count()
const adminCount = users.count({ role: 'admin' })

Mutation Methods (Trigger file writes)

These methods modify the collection and automatically save changes to disk:

insert(document): InsertResultT<DataT>

Inserts a single document. ID, createdDate, and updatedDate are automatically added.

const result = users.insert({
  name: 'Jane Smith',
  email: 'jane@example.com',
  age: 25,
})

if (result.success) {
  console.log('Inserted:', result.document)
} else {
  console.error('Error:', result.error)
}

Return Type:

interface InsertResultT<DataT> {
  success: boolean
  document: DataT
  error?: string
}
insertMany(documents): BulkInsertResultT<DataT>

Inserts multiple documents in a single operation.

const result = users.insertMany([
  { name: 'Alice', email: 'alice@example.com', age: 28 },
  { name: 'Bob', email: 'bob@example.com', age: 32 },
])

console.log(`Inserted ${result.insertedCount} documents`)
if (result.errors) {
  console.log('Errors:', result.errors)
}

Return Type:

interface BulkInsertResultT<DataT> {
  success: boolean
  insertedCount: number
  documents: DataT[]
  errors?: string[]
}
update(updates): UpdateResultT<DataT>

Updates documents by ID. Can accept a single update object or an array.

// Single update
const result = users.update({
  id: 'user-id-123',
  age: 31,
})

// Multiple updates
const result = users.update([
  { id: 'user-id-123', age: 31 },
  { id: 'user-id-456', name: 'Updated Name' },
])

Return Type:

interface UpdateResultT<DataT> {
  success: boolean
  matchedCount: number
  modifiedCount: number
  documents: DataT[]
  error?: string
}
updateMany(query, updates): UpdateResultT<DataT>

Updates all documents matching the query with the provided updates.

const result = users.updateMany(
  { age: { $lt: 18 } }, // Query: users under 18
  { category: 'minor' }, // Updates to apply
)
remove(query): RemoveResultT

Removes all documents matching the query.

const result = users.remove({ age: { $lt: 13 } })
console.log(`Deleted ${result.deletedCount} documents`)

Return Type:

interface RemoveResultT {
  success: boolean
  deletedCount: number
  error?: string
}
removeById(id: string): RemoveResultT

Removes a single document by ID.

const result = users.removeById('user-id-123')
removeByIds(ids: string[]): RemoveResultT

Removes multiple documents by their IDs.

const result = users.removeByIds(['id1', 'id2', 'id3'])
clear(): RemoveResultT

Removes all documents from the collection.

const result = users.clear()
console.log(`Cleared ${result.deletedCount} documents`)

Utility Methods

reload(): void

Reloads the collection data from the file, discarding any unsaved changes in memory.

users.reload()
getStats()

Returns statistics and metadata about the collection.

const stats = users.getStats()
console.log(`Documents: ${stats.documentCount}`)
console.log(`File: ${stats.filePath}`)
console.log(`Size: ${stats.fileSize} bytes`)
console.log(`Last modified: ${stats.lastModified}`)

Return Type:

interface Stats {
  documentCount: number
  filePath: string
  fileSize: number
  lastModified: Date | null
}

listerineDB Class

The main database instance for managing multiple collections.

createCollection<DataT>(options): Collection<DataT>

Creates a new collection with the specified options.

const users = listerineDB.createCollection<User>({
  filePath: './data/users.json',
  defaultData: [{ id: '1', name: 'Admin', email: 'admin@example.com', age: 30 }],
})
getCollection<DataT>(filePath): Collection<DataT> | undefined

Retrieves an existing collection by its file path.

const existingUsers = listerineDB.getCollection<User>('./data/users.json')
close(): void

Closes the database and clears all collection references.

listerineDB.close()

Query Syntax

listerineDB uses the listerine library for querying. Here are common query patterns:

Basic Equality

users.find({ name: 'John' })
users.find({ age: 30, active: true })

Comparison Operators

users.find({ age: { $gt: 18 } }) // Greater than
users.find({ age: { $gte: 18 } }) // Greater than or equal
users.find({ age: { $lt: 65 } }) // Less than
users.find({ age: { $lte: 65 } }) // Less than or equal
users.find({ age: { $ne: 30 } }) // Not equal

Array Operations

users.find({ age: { $in: [18, 21, 25] } }) // In array
users.find({ age: { $nin: [18, 21, 25] } }) // Not in array

Logical Operators

users.find({
  $or: [{ age: { $lt: 18 } }, { age: { $gt: 65 } }],
})

users.find({
  $and: [{ age: { $gte: 18 } }, { active: true }],
})

Error Handling

All mutation operations return result objects with success flags and error messages:

const result = users.insert({ name: 'Test User' })

if (!result.success) {
  console.error('Insert failed:', result.error)
  return
}

console.log('Inserted successfully:', result.document)

Best Practices

  1. Define TypeScript Interfaces: Always define proper interfaces extending BaseDocumentT
  2. Handle Errors: Check the success property in operation results
  3. Use Appropriate Queries: Use specific queries instead of loading all data when possible
  4. File Path Management: Use absolute paths or consistent relative paths for collections
  5. Backup Strategy: Consider implementing backups for important data files

Examples

User Management System

interface User extends BaseDocumentT {
  username: string
  email: string
  role: 'admin' | 'user'
  lastLogin?: number
}

const users = listerineDB.createCollection<User>({
  filePath: './data/users.json',
})

// Create admin user
users.insert({
  username: 'admin',
  email: 'admin@example.com',
  role: 'admin',
})

// Find all administrators
const admins = users.find({ role: 'admin' })

// Update last login time
users.updateMany({ username: 'admin' }, { lastLogin: Date.now() })

Product Catalog

interface Product extends BaseDocumentT {
  name: string
  price: number
  category: string
  inStock: boolean
  tags: string[]
}

const products = listerineDB.createCollection<Product>({
  filePath: './data/products.json',
})

// Add products
products.insertMany([
  { name: 'Laptop', price: 999, category: 'Electronics', inStock: true, tags: ['computer', 'portable'] },
  { name: 'Mouse', price: 29, category: 'Electronics', inStock: true, tags: ['computer', 'accessory'] },
])

// Find affordable electronics
const affordableElectronics = products.find({
  category: 'Electronics',
  price: { $lt: 100 },
})

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published