gitoriaLog in with ident

mpackdb

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commitd47876a1d47876a1reimplemented lost features like indexed find and more testscaramboleyod47876a1/tests/all-index-cases.test.js

10.0 KB

  1. import MPackDB, { PrimaryKeyType, IndexType } from '../src/MPackDB.js';
  2. import { rm } from 'fs/promises';
  3. const TEST_DB_PATH = 'db/cases';
  4. console.log('='.repeat(60));
  5. console.log('Comprehensive Index Test - All Cases');
  6. console.log('='.repeat(60));
  7. // Clean up
  8. await rm(TEST_DB_PATH, { recursive: true, force: true });
  9. // ============================================================================
  10. // Case 1: No primary key, no indexes
  11. // ============================================================================
  12. console.log('\n📋 Case 1: No primary key, no indexes');
  13. console.log('-'.repeat(60));
  14. const db1 = new MPackDB(`${TEST_DB_PATH}/case1`);
  15. await db1.insert({ name: 'Alice', age: 30 });
  16. await db1.insert({ name: 'Bob', age: 25 });
  17. await db1.insert({ name: 'Charlie', age: 35 });
  18. const all1 = await db1.find();
  19. console.log(`✓ Inserted 3 records, found ${all1.length}`);
  20. // Find with query function (no index)
  21. const filtered1 = await db1.find(r => r.age > 28);
  22. console.log(`✓ Query function (age > 28): found ${filtered1.length} records`);
  23. console.log(` Records: ${filtered1.map(r => r.name).join(', ')}`);
  24. await db1.close();
  25. // ============================================================================
  26. // Case 2: Numeric primary key only (auto-indexed)
  27. // ============================================================================
  28. console.log('\n📋 Case 2: Numeric primary key only (auto-indexed)');
  29. console.log('-'.repeat(60));
  30. const db2 = new MPackDB(`${TEST_DB_PATH}/case2`, {
  31. primaryKey: 'id',
  32. primaryKeyType: PrimaryKeyType.NUMBER,
  33. indexPersistThreshold: 2 // Low threshold for testing
  34. });
  35. await db2.insert({ name: 'Alice', age: 30 });
  36. await db2.insert({ name: 'Bob', age: 25 });
  37. await db2.insert({ name: 'Charlie', age: 35 });
  38. // Wait for auto-persist
  39. await new Promise(resolve => setTimeout(resolve, 100));
  40. // Find by primary key (uses index)
  41. const byId = await db2.find(1);
  42. console.log(`✓ Find by ID (1): ${byId[0]?.name}`);
  43. // Query function (no index for age)
  44. const filtered2 = await db2.find(r => r.age > 28);
  45. console.log(`✓ Query function (age > 28): found ${filtered2.length} records`);
  46. await db2.close();
  47. // ============================================================================
  48. // Case 3: UUID primary key only (auto-indexed, lexical)
  49. // ============================================================================
  50. console.log('\n📋 Case 3: UUID primary key only (auto-indexed, lexical)');
  51. console.log('-'.repeat(60));
  52. const db3 = new MPackDB(`${TEST_DB_PATH}/case3`, {
  53. primaryKey: 'uuid',
  54. primaryKeyType: PrimaryKeyType.UUID,
  55. indexPersistThreshold: 2
  56. });
  57. await db3.insert({ uuid: 'aaa-111', name: 'Alice', age: 30 });
  58. await db3.insert({ uuid: 'bbb-222', name: 'Bob', age: 25 });
  59. await db3.insert({ uuid: 'ccc-333', name: 'Charlie', age: 35 });
  60. await new Promise(resolve => setTimeout(resolve, 100));
  61. // Find by UUID
  62. const byUuid = await db3.find('bbb-222');
  63. console.log(`✓ Find by UUID (bbb-222): ${byUuid[0]?.name}`);
  64. await db3.close();
  65. // ============================================================================
  66. // Case 4: String primary key only (auto-indexed, lexical)
  67. // ============================================================================
  68. console.log('\n📋 Case 4: String primary key only (auto-indexed, lexical)');
  69. console.log('-'.repeat(60));
  70. const db4 = new MPackDB(`${TEST_DB_PATH}/case4`, {
  71. primaryKey: 'username',
  72. primaryKeyType: PrimaryKeyType.STRING,
  73. indexPersistThreshold: 2
  74. });
  75. await db4.insert({ username: 'alice123', name: 'Alice', age: 30 });
  76. await db4.insert({ username: 'bob456', name: 'Bob', age: 25 });
  77. await db4.insert({ username: 'charlie789', name: 'Charlie', age: 35 });
  78. await new Promise(resolve => setTimeout(resolve, 100));
  79. // Find by username
  80. const byUsername = await db4.find('bob456');
  81. console.log(`✓ Find by username (bob456): ${byUsername[0]?.name}`);
  82. await db4.close();
  83. // ============================================================================
  84. // Case 5: One lexical index only (no primary key)
  85. // ============================================================================
  86. console.log('\n📋 Case 5: One lexical index only (no primary key)');
  87. console.log('-'.repeat(60));
  88. const db5 = new MPackDB(`${TEST_DB_PATH}/case5`, {
  89. indexes: ['email'], // Lexical by default
  90. indexPersistThreshold: 2
  91. });
  92. await db5.insert({ name: 'Alice', email: '[email protected]', age: 30 });
  93. await db5.insert({ name: 'Bob', email: '[email protected]', age: 25 });
  94. await db5.insert({ name: 'Charlie', email: '[email protected]', age: 35 });
  95. await new Promise(resolve => setTimeout(resolve, 100));
  96. // Find using indexed field
  97. const byEmail = await db5.find(r => r.email === '[email protected]');
  98. console.log(`✓ Find by email ([email protected]): ${byEmail[0]?.name}`);
  99. // Query on non-indexed field
  100. const byAge = await db5.find(r => r.age > 28);
  101. console.log(`✓ Query on non-indexed field (age > 28): found ${byAge.length} records`);
  102. await db5.close();
  103. // ============================================================================
  104. // Case 6: One numeric index only (no primary key)
  105. // ============================================================================
  106. console.log('\n📋 Case 6: One numeric index only (no primary key)');
  107. console.log('-'.repeat(60));
  108. const db6 = new MPackDB(`${TEST_DB_PATH}/case6`, {
  109. indexes: ['*score'], // * prefix = numeric index
  110. indexPersistThreshold: 2
  111. });
  112. await db6.insert({ name: 'Alice', score: 95 });
  113. await db6.insert({ name: 'Bob', score: 87 });
  114. await db6.insert({ name: 'Charlie', score: 92 });
  115. await new Promise(resolve => setTimeout(resolve, 100));
  116. // Find using numeric indexed field
  117. const byScore = await db6.find(r => r.score >= 90);
  118. console.log(`✓ Find by score (>= 90): found ${byScore.length} records`);
  119. console.log(` Records: ${byScore.map(r => r.name).join(', ')}`);
  120. await db6.close();
  121. // ============================================================================
  122. // Case 7: Multiple indexes (mixed types)
  123. // ============================================================================
  124. console.log('\n📋 Case 7: Multiple indexes (mixed types)');
  125. console.log('-'.repeat(60));
  126. const db7 = new MPackDB(`${TEST_DB_PATH}/case7`, {
  127. primaryKey: 'id',
  128. primaryKeyType: PrimaryKeyType.NUMBER,
  129. indexes: ['email', '*age'], // email=lexical, age=numeric
  130. indexPersistThreshold: 2
  131. });
  132. await db7.insert({ name: 'Alice', email: '[email protected]', age: 30 });
  133. await db7.insert({ name: 'Bob', email: '[email protected]', age: 25 });
  134. await db7.insert({ name: 'Charlie', email: '[email protected]', age: 35 });
  135. await new Promise(resolve => setTimeout(resolve, 100));
  136. // Find by primary key
  137. const byId7 = await db7.find(0);
  138. console.log(`✓ Find by ID (0): ${byId7[0]?.name}`);
  139. // Find by lexical index
  140. const byEmail7 = await db7.find(r => r.email === '[email protected]');
  141. console.log(`✓ Find by email: ${byEmail7[0]?.name}`);
  142. // Find by numeric index
  143. const byAge7 = await db7.find(r => r.age >= 30);
  144. console.log(`✓ Find by age (>= 30): found ${byAge7.length} records`);
  145. await db7.close();
  146. // ============================================================================
  147. // Case 8: No primary key, multiple indexes
  148. // ============================================================================
  149. console.log('\n📋 Case 8: No primary key, multiple indexes');
  150. console.log('-'.repeat(60));
  151. const db8 = new MPackDB(`${TEST_DB_PATH}/case8`, {
  152. indexes: ['category', '*price'], // category=lexical, price=numeric
  153. indexPersistThreshold: 2
  154. });
  155. await db8.insert({ product: 'Laptop', category: 'Electronics', price: 999 });
  156. await db8.insert({ product: 'Mouse', category: 'Electronics', price: 25 });
  157. await db8.insert({ product: 'Desk', category: 'Furniture', price: 299 });
  158. await db8.insert({ product: 'Chair', category: 'Furniture', price: 150 });
  159. await new Promise(resolve => setTimeout(resolve, 100));
  160. // Find by lexical index
  161. const electronics = await db8.find(r => r.category === 'Electronics');
  162. console.log(`✓ Find by category (Electronics): found ${electronics.length} records`);
  163. // Find by numeric index
  164. const expensive = await db8.find(r => r.price > 200);
  165. console.log(`✓ Find by price (> 200): found ${expensive.length} records`);
  166. console.log(` Products: ${expensive.map(r => r.product).join(', ')}`);
  167. // Combined query (no single index can help)
  168. const expensiveElectronics = await db8.find(r => r.category === 'Electronics' && r.price > 500);
  169. console.log(`✓ Combined query: found ${expensiveElectronics.length} records`);
  170. await db8.close();
  171. // ============================================================================
  172. // Verify all index files were created and persisted
  173. // ============================================================================
  174. console.log('\n📋 Verify Index Files');
  175. console.log('-'.repeat(60));
  176. const { readdirSync, statSync } = await import('fs');
  177. const cases = [
  178. { name: 'Case 1 (no indexes)', prefix: `${TEST_DB_PATH}/case1`, expected: 0 },
  179. { name: 'Case 2 (id only)', prefix: `${TEST_DB_PATH}/case2`, expected: 1 },
  180. { name: 'Case 3 (_id only)', prefix: `${TEST_DB_PATH}/case3`, expected: 1 },
  181. { name: 'Case 4 (username only)', prefix: `${TEST_DB_PATH}/case4`, expected: 1 },
  182. { name: 'Case 5 (email only)', prefix: `${TEST_DB_PATH}/case5`, expected: 1 },
  183. { name: 'Case 6 (score only)', prefix: `${TEST_DB_PATH}/case6`, expected: 1 },
  184. { name: 'Case 7 (id+email+age)', prefix: `${TEST_DB_PATH}/case7`, expected: 3 },
  185. { name: 'Case 8 (category+price)', prefix: `${TEST_DB_PATH}/case8`, expected: 2 },
  186. ];
  187. for (const testCase of cases) {
  188. // List all files in cases directory matching the prefix
  189. const allFiles = readdirSync(TEST_DB_PATH);
  190. const indexFiles = allFiles.filter(f =>
  191. f.startsWith(testCase.prefix.replace(`${TEST_DB_PATH}/`, '')) &&
  192. f.endsWith('.txt')
  193. );
  194. const nonEmpty = indexFiles.filter(f => {
  195. const size = statSync(`${TEST_DB_PATH}/${f}`).size;
  196. return size > 0;
  197. });
  198. const status = nonEmpty.length === testCase.expected ? '✓' : '✗';
  199. console.log(`${status} ${testCase.name}: ${nonEmpty.length}/${testCase.expected} index files with data`);
  200. if (nonEmpty.length > 0) {
  201. console.log(` Files: ${nonEmpty.join(', ')}`);
  202. }
  203. }
  204. console.log('\n' + '='.repeat(60));
  205. console.log('✓ All index cases tested successfully!');
  206. console.log('='.repeat(60));

Branches

Latest commits

  • d47876a1reimplemented lost features like indexed find and more testscaramboleyo
  • 7f08da9afixed insert ignoring model definitioncaramboleyo
  • 705774a9added flush before findcaramboleyo
  • b4db6391initial commitcaramboleyo