smartdata/test/test.search.advanced.ts

187 lines
5.0 KiB
TypeScript

import { tap, expect } from '@push.rocks/tapbundle';
import * as smartmongo from '@push.rocks/smartmongo';
import * as smartdata from '../ts/index.js';
import { searchable } from '../ts/classes.doc.js';
import { smartunique } from '../ts/plugins.js';
// Set up database connection
let smartmongoInstance: smartmongo.SmartMongo;
let testDb: smartdata.SmartdataDb;
// Define a test class for advanced search scenarios
@smartdata.Collection(() => testDb)
class Product extends smartdata.SmartDataDbDoc<Product, Product> {
@smartdata.unI()
public id: string = smartunique.shortId();
@smartdata.svDb()
@searchable()
public name: string;
@smartdata.svDb()
@searchable()
public description: string;
@smartdata.svDb()
@searchable()
public category: string;
@smartdata.svDb()
public price: number;
constructor(
nameArg: string,
descriptionArg: string,
categoryArg: string,
priceArg: number,
) {
super();
this.name = nameArg;
this.description = descriptionArg;
this.category = categoryArg;
this.price = priceArg;
}
}
// Initialize DB and insert sample products
tap.test('setup advanced search database', async () => {
smartmongoInstance = await smartmongo.SmartMongo.createAndStart();
testDb = new smartdata.SmartdataDb(
await smartmongoInstance.getMongoDescriptor(),
);
await testDb.init();
});
tap.test('insert products for advanced search', async () => {
const products = [
new Product(
'Night Owl Lamp',
'Bright lamp for night reading',
'Lighting',
29,
),
new Product(
'Day Light Lamp',
'Daytime lamp with adjustable brightness',
'Lighting',
39,
),
new Product(
'Office Chair',
'Ergonomic chair for office',
'Furniture',
199,
),
new Product(
'Gaming Chair',
'Comfortable for long gaming sessions',
'Furniture',
299,
),
new Product(
'iPhone 12',
'Latest iPhone with A14 Bionic chip',
'Electronics',
999,
),
new Product(
'AirPods',
'Wireless earbuds with noise cancellation',
'Electronics',
249,
),
];
for (const p of products) {
await p.save();
}
const all = await Product.getInstances({});
expect(all.length).toEqual(products.length);
});
// Simple exact field:value matching
tap.test('simpleExact: category:Furniture returns chairs', async () => {
const res = await Product.search('category:Furniture');
expect(res.length).toEqual(2);
const names = res.map((r) => r.name).sort();
expect(names).toEqual(['Gaming Chair', 'Office Chair']);
});
// simpleExact invalid field should throw
tap.test('simpleExact invalid field errors', async () => {
let error: Error;
try {
await Product.search('price:29');
} catch (e) {
error = e as Error;
}
expect(error).toBeTruthy();
expect(error.message).toMatch(/not searchable/);
});
// Quoted phrase search
tap.test('quoted phrase "Bright lamp" matches Night Owl Lamp', async () => {
const res = await Product.search('"Bright lamp"');
expect(res.length).toEqual(1);
expect(res[0].name).toEqual('Night Owl Lamp');
});
tap.test("quoted phrase 'night reading' matches Night Owl Lamp", async () => {
const res = await Product.search("'night reading'");
expect(res.length).toEqual(1);
expect(res[0].name).toEqual('Night Owl Lamp');
});
tap.test('wildcard description:*gaming* matches Gaming Chair', async () => {
const res = await Product.search('description:*gaming*');
expect(res.length).toEqual(1);
expect(res[0].name).toEqual('Gaming Chair');
});
// Boolean AND and OR
tap.test('boolean AND: category:Lighting AND lamp', async () => {
const res = await Product.search('category:Lighting AND lamp');
expect(res.length).toEqual(2);
});
tap.test('boolean OR: Furniture OR Electronics', async () => {
const res = await Product.search('Furniture OR Electronics');
expect(res.length).toEqual(4);
});
// Multi-term unquoted -> AND across terms
tap.test('multi-term unquoted adjustable brightness', async () => {
const res = await Product.search('adjustable brightness');
expect(res.length).toEqual(1);
expect(res[0].name).toEqual('Day Light Lamp');
});
tap.test('multi-term unquoted Night Lamp', async () => {
const res = await Product.search('Night Lamp');
expect(res.length).toEqual(1);
expect(res[0].name).toEqual('Night Owl Lamp');
});
// Grouping with parentheses
tap.test('grouping: (Furniture OR Electronics) AND Chair', async () => {
const res = await Product.search(
'(Furniture OR Electronics) AND Chair',
);
expect(res.length).toEqual(2);
const names = res.map((r) => r.name).sort();
expect(names).toEqual(['Gaming Chair', 'Office Chair']);
});
// Teardown
tap.test('cleanup advanced search database', async () => {
await testDb.mongoDb.dropDatabase();
await testDb.close();
if (smartmongoInstance) {
await smartmongoInstance.stopAndDumpToDir(
`.nogit/dbdump/test.search.advanced.ts`,
);
}
setTimeout(() => process.exit(), 2000);
});
tap.start({ throwOnError: true });