From e325b42906a7ae097da0902f18a843c034f70d69 Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Fri, 18 Apr 2025 14:56:11 +0000 Subject: [PATCH] fix(search): Refactor search tests to use unified search API and update text index type casting --- changelog.md | 6 +++++ test/test.search.ts | 57 ++++++++++++++++++++++++++++++---------- ts/00_commitinfo_data.ts | 2 +- ts/classes.collection.ts | 6 +++-- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index 0fabc3c..46c0900 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 2025-04-18 - 5.9.1 - fix(search) +Refactor search tests to use unified search API and update text index type casting + +- Replaced all calls from searchWithLucene with search in test/search tests +- Updated text index specification in the collection class to use proper type casting + ## 2025-04-18 - 5.9.0 - feat(collections/search) Improve text index creation and search fallback mechanisms in collections and document search methods diff --git a/test/test.search.ts b/test/test.search.ts index ccc3c12..7237d29 100644 --- a/test/test.search.ts +++ b/test/test.search.ts @@ -104,21 +104,21 @@ tap.test('should search products by basic search method', async () => { } }); -tap.test('should search products with searchWithLucene method', async () => { +tap.test('should search products with search method', async () => { // Using the robust searchWithLucene method - const wirelessResults = await Product.searchWithLucene('wireless'); - console.log( - `Found ${wirelessResults.length} products matching 'wireless' using searchWithLucene`, - ); + const wirelessResults = await Product.search('wireless'); + console.log( + `Found ${wirelessResults.length} products matching 'wireless' using search`, + ); expect(wirelessResults.length).toEqual(1); expect(wirelessResults[0].name).toEqual('AirPods'); }); -tap.test('should search products by category with searchWithLucene', async () => { +tap.test('should search products by category with search', async () => { // Using field-specific search with searchWithLucene - const kitchenResults = await Product.searchWithLucene('category:Kitchen'); - console.log(`Found ${kitchenResults.length} products in Kitchen category using searchWithLucene`); + const kitchenResults = await Product.search('category:Kitchen'); + console.log(`Found ${kitchenResults.length} products in Kitchen category using search`); expect(kitchenResults.length).toEqual(2); expect(kitchenResults[0].category).toEqual('Kitchen'); @@ -127,7 +127,7 @@ tap.test('should search products by category with searchWithLucene', async () => tap.test('should search products with partial word matches', async () => { // Testing partial word matches - const proResults = await Product.searchWithLucene('Pro'); + const proResults = await Product.search('Pro'); console.log(`Found ${proResults.length} products matching 'Pro'`); // Should match both "MacBook Pro" and "professionals" in description @@ -136,7 +136,7 @@ tap.test('should search products with partial word matches', async () => { tap.test('should search across multiple searchable fields', async () => { // Test searching across all searchable fields - const bookResults = await Product.searchWithLucene('book'); + const bookResults = await Product.search('book'); console.log(`Found ${bookResults.length} products matching 'book' across all fields`); // Should match "MacBook" in name and "Books" in category @@ -145,8 +145,8 @@ tap.test('should search across multiple searchable fields', async () => { tap.test('should handle case insensitive searches', async () => { // Test case insensitivity - const electronicsResults = await Product.searchWithLucene('electronics'); - const ElectronicsResults = await Product.searchWithLucene('Electronics'); + const electronicsResults = await Product.search('electronics'); + const ElectronicsResults = await Product.search('Electronics'); console.log(`Found ${electronicsResults.length} products matching lowercase 'electronics'`); console.log(`Found ${ElectronicsResults.length} products matching capitalized 'Electronics'`); @@ -166,14 +166,14 @@ tap.test('should demonstrate search fallback mechanisms', async () => { // Use a simpler term that should be found in descriptions // Avoid using "OR" operator which requires a text index - const results = await Product.searchWithLucene('high'); + const results = await Product.search('high'); console.log(`Found ${results.length} products matching 'high'`); // "High-speed blender" contains "high" expect(results.length).toBeGreaterThan(0); // Try another fallback example that won't need $text - const powerfulResults = await Product.searchWithLucene('powerful'); + const powerfulResults = await Product.search('powerful'); console.log(`Found ${powerfulResults.length} products matching 'powerful'`); // "Powerful laptop for professionals" contains "powerful" @@ -192,6 +192,35 @@ tap.test('should explain the advantages of the integrated approach', async () => expect(true).toEqual(true); }); +// Additional robustness tests +tap.test('should search exact name using field:value', async () => { + const nameResults = await Product.search('name:AirPods'); + expect(nameResults.length).toEqual(1); + expect(nameResults[0].name).toEqual('AirPods'); +}); + +tap.test('should throw when searching non-searchable field', async () => { + let error: Error; + try { + await Product.search('price:129'); + } catch (err) { + error = err as Error; + } + expect(error).toBeTruthy(); + expect(error.message).toMatch(/not searchable/); +}); + +tap.test('empty query should return all products', async () => { + const allResults = await Product.search(''); + expect(allResults.length).toEqual(8); +}); + +tap.test('should search multi-word term across fields', async () => { + const termResults = await Product.search('iPhone 12'); + expect(termResults.length).toEqual(1); + expect(termResults[0].name).toEqual('iPhone 12'); +}); + tap.test('close database connection', async () => { await testDb.mongoDb.dropDatabase(); await testDb.close(); diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index a4bab67..4e1b060 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartdata', - version: '5.9.0', + version: '5.9.1', description: 'An advanced library for NoSQL data organization and manipulation using TypeScript with support for MongoDB, data validation, collections, and custom data types.' } diff --git a/ts/classes.collection.ts b/ts/classes.collection.ts index 7c0c591..04917d6 100644 --- a/ts/classes.collection.ts +++ b/ts/classes.collection.ts @@ -158,9 +158,11 @@ export class SmartdataCollection { // Auto-create a compound text index on all searchable fields const searchableFields = getSearchableFields(this.collectionName); if (searchableFields.length > 0 && !this.textIndexCreated) { - const indexSpec: { [key: string]: string } = {}; + // Build a compound text index spec + const indexSpec: Record = {}; searchableFields.forEach(f => { indexSpec[f] = 'text'; }); - await this.mongoDbCollection.createIndex(indexSpec, { name: 'smartdata_text_index' }); + // Cast to any to satisfy TypeScript IndexSpecification typing + await this.mongoDbCollection.createIndex(indexSpec as any, { name: 'smartdata_text_index' }); this.textIndexCreated = true; } }