fix(search): Refactor search tests to use unified search API and update text index type casting

This commit is contained in:
Philipp Kunz 2025-04-18 14:56:11 +00:00
parent 1a359d355a
commit e325b42906
4 changed files with 54 additions and 17 deletions

View File

@ -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

View File

@ -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();

View File

@ -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.'
}

View File

@ -158,9 +158,11 @@ export class SmartdataCollection<T> {
// 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<string, 'text'> = {};
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;
}
}