mpackdb
All repositories: gitoria
10.0 KB
import MPackDB, { PrimaryKeyType, IndexType } from '../src/MPackDB.js';import { rm } from 'fs/promises';const TEST_DB_PATH = 'db/cases';console.log('='.repeat(60));console.log('Comprehensive Index Test - All Cases');console.log('='.repeat(60));// Clean upawait rm(TEST_DB_PATH, { recursive: true, force: true });// ============================================================================// Case 1: No primary key, no indexes// ============================================================================console.log('\n📋 Case 1: No primary key, no indexes');console.log('-'.repeat(60));const db1 = new MPackDB(`${TEST_DB_PATH}/case1`);await db1.insert({ name: 'Alice', age: 30 });await db1.insert({ name: 'Bob', age: 25 });await db1.insert({ name: 'Charlie', age: 35 });const all1 = await db1.find();console.log(`✓ Inserted 3 records, found ${all1.length}`);// Find with query function (no index)const filtered1 = await db1.find(r => r.age > 28);console.log(`✓ Query function (age > 28): found ${filtered1.length} records`);console.log(` Records: ${filtered1.map(r => r.name).join(', ')}`);await db1.close();// ============================================================================// Case 2: Numeric primary key only (auto-indexed)// ============================================================================console.log('\n📋 Case 2: Numeric primary key only (auto-indexed)');console.log('-'.repeat(60));const db2 = new MPackDB(`${TEST_DB_PATH}/case2`, {primaryKey: 'id',primaryKeyType: PrimaryKeyType.NUMBER,indexPersistThreshold: 2 // Low threshold for testing});await db2.insert({ name: 'Alice', age: 30 });await db2.insert({ name: 'Bob', age: 25 });await db2.insert({ name: 'Charlie', age: 35 });// Wait for auto-persistawait new Promise(resolve => setTimeout(resolve, 100));// Find by primary key (uses index)const byId = await db2.find(1);console.log(`✓ Find by ID (1): ${byId[0]?.name}`);// Query function (no index for age)const filtered2 = await db2.find(r => r.age > 28);console.log(`✓ Query function (age > 28): found ${filtered2.length} records`);await db2.close();// ============================================================================// Case 3: UUID primary key only (auto-indexed, lexical)// ============================================================================console.log('\n📋 Case 3: UUID primary key only (auto-indexed, lexical)');console.log('-'.repeat(60));const db3 = new MPackDB(`${TEST_DB_PATH}/case3`, {primaryKey: 'uuid',primaryKeyType: PrimaryKeyType.UUID,indexPersistThreshold: 2});await db3.insert({ uuid: 'aaa-111', name: 'Alice', age: 30 });await db3.insert({ uuid: 'bbb-222', name: 'Bob', age: 25 });await db3.insert({ uuid: 'ccc-333', name: 'Charlie', age: 35 });await new Promise(resolve => setTimeout(resolve, 100));// Find by UUIDconst byUuid = await db3.find('bbb-222');console.log(`✓ Find by UUID (bbb-222): ${byUuid[0]?.name}`);await db3.close();// ============================================================================// Case 4: String primary key only (auto-indexed, lexical)// ============================================================================console.log('\n📋 Case 4: String primary key only (auto-indexed, lexical)');console.log('-'.repeat(60));const db4 = new MPackDB(`${TEST_DB_PATH}/case4`, {primaryKey: 'username',primaryKeyType: PrimaryKeyType.STRING,indexPersistThreshold: 2});await db4.insert({ username: 'alice123', name: 'Alice', age: 30 });await db4.insert({ username: 'bob456', name: 'Bob', age: 25 });await db4.insert({ username: 'charlie789', name: 'Charlie', age: 35 });await new Promise(resolve => setTimeout(resolve, 100));// Find by usernameconst byUsername = await db4.find('bob456');console.log(`✓ Find by username (bob456): ${byUsername[0]?.name}`);await db4.close();// ============================================================================// Case 5: One lexical index only (no primary key)// ============================================================================console.log('\n📋 Case 5: One lexical index only (no primary key)');console.log('-'.repeat(60));const db5 = new MPackDB(`${TEST_DB_PATH}/case5`, {indexes: ['email'], // Lexical by defaultindexPersistThreshold: 2});await db5.insert({ name: 'Alice', email: '[email protected]', age: 30 });await db5.insert({ name: 'Bob', email: '[email protected]', age: 25 });await db5.insert({ name: 'Charlie', email: '[email protected]', age: 35 });await new Promise(resolve => setTimeout(resolve, 100));// Find using indexed fieldconst byEmail = await db5.find(r => r.email === '[email protected]');console.log(`✓ Find by email ([email protected]): ${byEmail[0]?.name}`);// Query on non-indexed fieldconst byAge = await db5.find(r => r.age > 28);console.log(`✓ Query on non-indexed field (age > 28): found ${byAge.length} records`);await db5.close();// ============================================================================// Case 6: One numeric index only (no primary key)// ============================================================================console.log('\n📋 Case 6: One numeric index only (no primary key)');console.log('-'.repeat(60));const db6 = new MPackDB(`${TEST_DB_PATH}/case6`, {indexes: ['*score'], // * prefix = numeric indexindexPersistThreshold: 2});await db6.insert({ name: 'Alice', score: 95 });await db6.insert({ name: 'Bob', score: 87 });await db6.insert({ name: 'Charlie', score: 92 });await new Promise(resolve => setTimeout(resolve, 100));// Find using numeric indexed fieldconst byScore = await db6.find(r => r.score >= 90);console.log(`✓ Find by score (>= 90): found ${byScore.length} records`);console.log(` Records: ${byScore.map(r => r.name).join(', ')}`);await db6.close();// ============================================================================// Case 7: Multiple indexes (mixed types)// ============================================================================console.log('\n📋 Case 7: Multiple indexes (mixed types)');console.log('-'.repeat(60));const db7 = new MPackDB(`${TEST_DB_PATH}/case7`, {primaryKey: 'id',primaryKeyType: PrimaryKeyType.NUMBER,indexes: ['email', '*age'], // email=lexical, age=numericindexPersistThreshold: 2});await db7.insert({ name: 'Alice', email: '[email protected]', age: 30 });await db7.insert({ name: 'Bob', email: '[email protected]', age: 25 });await db7.insert({ name: 'Charlie', email: '[email protected]', age: 35 });await new Promise(resolve => setTimeout(resolve, 100));// Find by primary keyconst byId7 = await db7.find(0);console.log(`✓ Find by ID (0): ${byId7[0]?.name}`);// Find by lexical indexconst byEmail7 = await db7.find(r => r.email === '[email protected]');console.log(`✓ Find by email: ${byEmail7[0]?.name}`);// Find by numeric indexconst byAge7 = await db7.find(r => r.age >= 30);console.log(`✓ Find by age (>= 30): found ${byAge7.length} records`);await db7.close();// ============================================================================// Case 8: No primary key, multiple indexes// ============================================================================console.log('\n📋 Case 8: No primary key, multiple indexes');console.log('-'.repeat(60));const db8 = new MPackDB(`${TEST_DB_PATH}/case8`, {indexes: ['category', '*price'], // category=lexical, price=numericindexPersistThreshold: 2});await db8.insert({ product: 'Laptop', category: 'Electronics', price: 999 });await db8.insert({ product: 'Mouse', category: 'Electronics', price: 25 });await db8.insert({ product: 'Desk', category: 'Furniture', price: 299 });await db8.insert({ product: 'Chair', category: 'Furniture', price: 150 });await new Promise(resolve => setTimeout(resolve, 100));// Find by lexical indexconst electronics = await db8.find(r => r.category === 'Electronics');console.log(`✓ Find by category (Electronics): found ${electronics.length} records`);// Find by numeric indexconst expensive = await db8.find(r => r.price > 200);console.log(`✓ Find by price (> 200): found ${expensive.length} records`);console.log(` Products: ${expensive.map(r => r.product).join(', ')}`);// Combined query (no single index can help)const expensiveElectronics = await db8.find(r => r.category === 'Electronics' && r.price > 500);console.log(`✓ Combined query: found ${expensiveElectronics.length} records`);await db8.close();// ============================================================================// Verify all index files were created and persisted// ============================================================================console.log('\n📋 Verify Index Files');console.log('-'.repeat(60));const { readdirSync, statSync } = await import('fs');const cases = [{ name: 'Case 1 (no indexes)', prefix: `${TEST_DB_PATH}/case1`, expected: 0 },{ name: 'Case 2 (id only)', prefix: `${TEST_DB_PATH}/case2`, expected: 1 },{ name: 'Case 3 (_id only)', prefix: `${TEST_DB_PATH}/case3`, expected: 1 },{ name: 'Case 4 (username only)', prefix: `${TEST_DB_PATH}/case4`, expected: 1 },{ name: 'Case 5 (email only)', prefix: `${TEST_DB_PATH}/case5`, expected: 1 },{ name: 'Case 6 (score only)', prefix: `${TEST_DB_PATH}/case6`, expected: 1 },{ name: 'Case 7 (id+email+age)', prefix: `${TEST_DB_PATH}/case7`, expected: 3 },{ name: 'Case 8 (category+price)', prefix: `${TEST_DB_PATH}/case8`, expected: 2 },];for (const testCase of cases) {// List all files in cases directory matching the prefixconst allFiles = readdirSync(TEST_DB_PATH);const indexFiles = allFiles.filter(f =>f.startsWith(testCase.prefix.replace(`${TEST_DB_PATH}/`, '')) &&f.endsWith('.txt'));const nonEmpty = indexFiles.filter(f => {const size = statSync(`${TEST_DB_PATH}/${f}`).size;return size > 0;});const status = nonEmpty.length === testCase.expected ? '✓' : '✗';console.log(`${status} ${testCase.name}: ${nonEmpty.length}/${testCase.expected} index files with data`);if (nonEmpty.length > 0) {console.log(` Files: ${nonEmpty.join(', ')}`);}}console.log('\n' + '='.repeat(60));console.log('✓ All index cases tested successfully!');console.log('='.repeat(60));
Branches
- mastermain branch
Latest commits
- d01dda02add index hints, intersection, boundingBox; remove findByIndexcaramboleyo
- b8ffc1a0release 1.0.5caramboleyo
- d47876a1reimplemented lost features like indexed find and more testscaramboleyo
- 7f08da9afixed insert ignoring model definitioncaramboleyo
- 705774a9added flush before findcaramboleyo
- b4db6391initial commitcaramboleyo