From 64c25241de47df7c552425ae355ee8482628980a Mon Sep 17 00:00:00 2001 From: zetavg <mail@zeta.vg> Date: Sun, 19 Nov 2023 03:39:24 +0800 Subject: [PATCH] data-storage-couchdb: test if getData will use index as expected --- Data/lib/types.ts | 1 + .../__tests__/data-storage-couchdb.test.ts | 144 ++++++++++++++++++ .../lib/functions/getGetData.ts | 28 ++-- 3 files changed, 162 insertions(+), 11 deletions(-) diff --git a/Data/lib/types.ts b/Data/lib/types.ts index d24e16d9..030f4d58 100644 --- a/Data/lib/types.ts +++ b/Data/lib/types.ts @@ -85,6 +85,7 @@ export type GetData = <T extends DataTypeName>( skip?: number; limit?: number; sort?: SortOption<DataType<T>>; + debug?: boolean; }, ) => Promise<Array<ValidDataTypeWithID<T> | InvalidDataTypeWithID<T>>>; diff --git a/packages/data-storage-couchdb/lib/__tests__/data-storage-couchdb.test.ts b/packages/data-storage-couchdb/lib/__tests__/data-storage-couchdb.test.ts index fa6dea73..c9399821 100644 --- a/packages/data-storage-couchdb/lib/__tests__/data-storage-couchdb.test.ts +++ b/packages/data-storage-couchdb/lib/__tests__/data-storage-couchdb.test.ts @@ -258,6 +258,45 @@ describe('getData', () => { }); }); + it('will use index with sort', async () => { + await withContext(async context => { + const d = new CouchDBData(context); + for (let i = 1; i <= 10; i++) { + const collection = await d.saveDatum({ + __type: 'collection', + name: `Collection #${i}`, + icon_name: 'box', + icon_color: 'gray', + collection_reference_number: `${i}`, + __created_at: 11 - i, + }); + + await d.saveDatum({ + __type: 'item', + collection_id: collection.__id, + name: `Item #${i}`, + icon_name: 'box', + icon_color: 'gray', + __created_at: 21 - i, + }); + } + + const collections = await d.getData( + 'collection', + {}, + { sort: [{ __created_at: 'desc' }], debug: true }, + ); + expect((collections as any).debug_info.explain.index.ddoc).toBeTruthy(); + + const items = await d.getData( + 'item', + {}, + { sort: [{ __created_at: 'asc' }], debug: true }, + ); + expect((items as any).debug_info.explain.index.ddoc).toBeTruthy(); + }); + }); + it('works with sort and limit and skip', async () => { await withContext(async context => { const d = new CouchDBData(context); @@ -587,6 +626,66 @@ describe('getData', () => { }); }); + it('will use index', async () => { + await withContext(async context => { + const d = new CouchDBData(context); + for (let i = 1; i <= 10; i++) { + const collection = await d.saveDatum({ + __type: 'collection', + name: `Collection #${i}`, + icon_name: 'box', + icon_color: 'gray', + collection_reference_number: `${i}`, + }); + + await d.saveDatum({ + __type: 'item', + collection_id: collection.__id, + name: `Item #${i}`, + icon_name: 'box', + icon_color: 'gray', + model_name: `Model ${i <= 5 ? 'A' : 'B'}`, + }); + } + + const modelAItems = await d.getData( + 'item', + { model_name: 'Model A' }, + { debug: true }, + ); + expect((modelAItems as any).debug_info.explain.index.ddoc).toBeTruthy(); + + const modelBItems = await d.getData( + 'item', + { model_name: 'Model B' }, + { debug: true }, + ); + expect((modelBItems as any).debug_info.explain.index.ddoc).toBeTruthy(); + + const itemsCreatedAfter0 = await d.getData( + 'item', + { + __created_at: { $gt: 0 }, + }, + { debug: true }, + ); + expect( + (itemsCreatedAfter0 as any).debug_info.explain.index.ddoc, + ).toBeTruthy(); + + const collectionsCreatedAfter0 = await d.getData( + 'collection', + { + __created_at: { $gt: 0 }, + }, + { debug: true }, + ); + expect( + (collectionsCreatedAfter0 as any).debug_info.explain.index.ddoc, + ).toBeTruthy(); + }); + }); + it('works with limit and skip', async () => { await withContext(async context => { const d = new CouchDBData(context); @@ -692,6 +791,51 @@ describe('getData', () => { ); }); }); + + it('will use index with sort', async () => { + await withContext(async context => { + const d = new CouchDBData(context); + for (let i = 1; i <= 10; i++) { + const collection = await d.saveDatum({ + __type: 'collection', + name: `Collection #${i}`, + icon_name: 'box', + icon_color: 'gray', + collection_reference_number: `${i}`, + }); + + await d.saveDatum({ + __type: 'item', + collection_id: collection.__id, + name: `Item #${i}`, + icon_name: 'box', + icon_color: 'gray', + model_name: `Model ${i <= 5 ? 'A' : 'B'}`, + purchase_price_x1000: Math.abs(6 - i) * 1000, + }); + } + + const items_a = await d.getData( + 'item', + { model_name: 'Model A' }, + { sort: [{ purchase_price_x1000: 'asc' }], debug: true }, + ); + expect((items_a as any).debug_info.explain.index.ddoc).toBeTruthy(); + + const items_b = await d.getData( + 'item', + { model_name: 'Model B' }, + { + sort: [ + // { model_name: 'desc' }, // To prevent CouchDB error - { "error": "unsupported_mixed_sort", "reason": "Sorts currently only support a single direction for all fields.", "status": 400 } // This is now handled in getData + { purchase_price_x1000: 'desc' }, + ], + debug: true, + }, + ); + expect((items_b as any).debug_info.explain.index.ddoc).toBeTruthy(); + }); + }); }); describe('with field exists conditions', () => { diff --git a/packages/data-storage-couchdb/lib/functions/getGetData.ts b/packages/data-storage-couchdb/lib/functions/getGetData.ts index 9b11f152..7216c70c 100644 --- a/packages/data-storage-couchdb/lib/functions/getGetData.ts +++ b/packages/data-storage-couchdb/lib/functions/getGetData.ts @@ -26,7 +26,7 @@ export default function getGetData({ const getData: GetData = async function getData( type, conditions = {}, - { skip = 0, limit = undefined, sort } = {}, + { skip = 0, limit = undefined, sort, debug } = {}, ) { const logDebug = logLevels && logLevels().includes('debug'); @@ -223,7 +223,7 @@ export default function getGetData({ })(); // Since remote CouchDB server may not throw error if index not found - if (alwaysCreateIndexFirst && ddocName && index) { + if ((alwaysCreateIndexFirst || debug) && ddocName && index) { try { await db.createIndex({ ddoc: ddocName, @@ -233,25 +233,25 @@ export default function getGetData({ } catch (e) {} } - if (logger && logDebug) { - let explain = ''; + let explain: string | object = ''; + if (debug || (logger && logDebug)) { try { - explain = `, explain: ${JSON.stringify( - await ((db as any).explain as any)(query), - null, - 2, - )}`; + explain = await ((db as any).explain as any)(query); } catch (e) { - explain = `, explain: error on getting explain - ${ + explain = `error on getting explain - ${ e instanceof Error ? e.message : JSON.stringify(e) }`; } + } + if (logger && logDebug) { logger.debug( `getData query: ${JSON.stringify( query, null, 2, - )}, index: ${JSON.stringify(index, null, 2)}` + explain, + )}, index: ${JSON.stringify(index, null, 2)}, explain: ${ + typeof explain === 'string' ? explain : JSON.stringify(explain) + }`, ); } @@ -318,6 +318,12 @@ export default function getGetData({ ); } + if (debug) { + data.debug_info = { + explain, + }; + } + return data; };