From c7e25b0e7db11bbdd44750bdce88b2170cc66ce6 Mon Sep 17 00:00:00 2001 From: Gareth Johnson Date: Thu, 24 Apr 2025 15:34:12 +0100 Subject: [PATCH 1/3] add relative haystack filter generation --- spec/filter/util.spec.ts | 60 +++++++++++++++++++++++++++++++ src/filter/util.ts | 76 ++++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + 3 files changed, 137 insertions(+) create mode 100644 spec/filter/util.spec.ts create mode 100644 src/filter/util.ts diff --git a/spec/filter/util.spec.ts b/spec/filter/util.spec.ts new file mode 100644 index 0000000..acd00c5 --- /dev/null +++ b/spec/filter/util.spec.ts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025, J2 Innovations. All Rights Reserved + */ + +import { HDict } from '../../src/core/dict/HDict' +import { HRef } from '../../src/core/HRef' +import { makeRelativeHaystackFilter } from '../../src/filter/util' +import { HMarker } from '../../src/core/HMarker' +import { HStr } from '../../src/core/HStr' + +describe('haystack', () => { + describe('makeRelativeHaystackFilter()', () => { + it('returns a haystack filter with dis', () => { + expect( + makeRelativeHaystackFilter( + new HDict({ + id: HRef.make('id'), + dis: 'an equip', + equip: HMarker.make(), + }) + ) + ).toEqual('equip and dis == "an equip"') + }) + + it('returns a haystack filter with a nav name', () => { + expect( + makeRelativeHaystackFilter( + new HDict({ + id: HRef.make('id'), + navName: 'an equip', + equip: HMarker.make(), + }) + ) + ).toEqual('equip and navName == "an equip"') + }) + + it('returns a haystack filter with a point kind', () => { + expect( + makeRelativeHaystackFilter( + new HDict({ + id: HRef.make('id'), + navName: 'a point', + point: HMarker.make(), + kind: HStr.make('Number'), + }) + ) + ).toEqual('point and navName == "a point" and kind == "Number"') + }) + + it('returns a haystack filter with an absolute id as a fallback', () => { + expect( + makeRelativeHaystackFilter( + new HDict({ + id: HRef.make('id'), + }) + ) + ).toEqual('id == @id') + }) + }) // makeRelativeHaystackFilter() +}) diff --git a/src/filter/util.ts b/src/filter/util.ts new file mode 100644 index 0000000..194cd9a --- /dev/null +++ b/src/filter/util.ts @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025, J2 Innovations. All Rights Reserved + */ + +import { HDict } from '../core/dict/HDict' +import { HMarker } from '../core/HMarker' +import { HRef } from '../core/HRef' +import { HStr } from '../core/HStr' +import { valueIsKind } from '../core/HVal' +import { Kind } from '../core/Kind' +import { HFilterBuilder } from '../filter/HFilterBuilder' + +/** + * Makes a relative haystack filter from a record. + * + * @param record The record. + * @returns A haystack filter. + */ +export function makeRelativeHaystackFilter(record: HDict): string { + const builder = new HFilterBuilder() + + // Build the marker tags. + for (const { name, value } of record) { + if (valueIsKind(value, Kind.Marker)) { + if (!builder.isEmpty()) { + builder.and() + } + + builder.has(name) + } + } + + // Build the display name match if one is available. + if (!addDisplayNameToFilter(record, builder, 'dis')) { + if (!addDisplayNameToFilter(record, builder, 'name')) { + if (!addDisplayNameToFilter(record, builder, 'tag')) { + addDisplayNameToFilter(record, builder, 'navName') + } + } + } + + // Include a point's kind if available. + if (record.has('point') && record.has('kind')) { + const kind = record.get('kind')?.value + if (kind) { + if (!builder.isEmpty()) { + builder.and() + } + builder.equals('kind', kind) + } + } + + if (builder.isEmpty() && record.has('id')) { + builder.equals('id', record.get('id') as HRef) + } + + return builder.build() +} + +function addDisplayNameToFilter( + record: HDict, + builder: HFilterBuilder, + tagName: string +): boolean { + // Build the display name match. + const name = record.get(tagName)?.value + if (name) { + if (!builder.isEmpty()) { + builder.and() + } + + builder.equals(tagName, name) + return true + } + return false +} diff --git a/src/index.ts b/src/index.ts index 70e1188..e8d5946 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,6 +21,7 @@ export * from './filter/tokens' export * from './filter/TokenType' export * from './filter/TokenValue' export * from './filter/HFilterBuilder' +export * from './filter/util' // Util export * from './util/LocalizedError' From 94e34fa4f0e457baec13a6b496df5e5f544a1181 Mon Sep 17 00:00:00 2001 From: Gareth Johnson Date: Thu, 24 Apr 2025 16:19:13 +0100 Subject: [PATCH 2/3] Add relativization options --- spec/filter/util.spec.ts | 31 +++++++++++++++++++++++++++++++ src/filter/util.ts | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/spec/filter/util.spec.ts b/spec/filter/util.spec.ts index acd00c5..1d4a450 100644 --- a/spec/filter/util.spec.ts +++ b/spec/filter/util.spec.ts @@ -22,6 +22,21 @@ describe('haystack', () => { ).toEqual('equip and dis == "an equip"') }) + it('returns a haystack filter without a display name with the option disabled', () => { + expect( + makeRelativeHaystackFilter( + new HDict({ + id: HRef.make('id'), + dis: 'an equip', + equip: HMarker.make(), + }), + { + useDisplayName: false, + } + ) + ).toEqual('equip') + }) + it('returns a haystack filter with a nav name', () => { expect( makeRelativeHaystackFilter( @@ -47,6 +62,22 @@ describe('haystack', () => { ).toEqual('point and navName == "a point" and kind == "Number"') }) + it('returns a haystack filter without the point kind and the option disabled', () => { + expect( + makeRelativeHaystackFilter( + new HDict({ + id: HRef.make('id'), + navName: 'a point', + point: HMarker.make(), + kind: HStr.make('Number'), + }), + { + useKind: false, + } + ) + ).toEqual('point and navName == "a point"') + }) + it('returns a haystack filter with an absolute id as a fallback', () => { expect( makeRelativeHaystackFilter( diff --git a/src/filter/util.ts b/src/filter/util.ts index 194cd9a..ea79966 100644 --- a/src/filter/util.ts +++ b/src/filter/util.ts @@ -10,13 +10,34 @@ import { valueIsKind } from '../core/HVal' import { Kind } from '../core/Kind' import { HFilterBuilder } from '../filter/HFilterBuilder' +/** + * Relativization options. + */ +export interface RelativizeOptions { + /** + * True (or undefined) if the display name should be used in the relativization. + */ + useDisplayName?: boolean + + /** + * True (or undefined) if the point's kind should be used in the relativization. + */ + useKind?: boolean +} + /** * Makes a relative haystack filter from a record. * * @param record The record. * @returns A haystack filter. */ -export function makeRelativeHaystackFilter(record: HDict): string { +export function makeRelativeHaystackFilter( + record: HDict, + options?: RelativizeOptions +): string { + const useDisplayName = options?.useDisplayName ?? true + const useKind = options?.useKind ?? true + const builder = new HFilterBuilder() // Build the marker tags. @@ -30,17 +51,19 @@ export function makeRelativeHaystackFilter(record: HDict): string { } } - // Build the display name match if one is available. - if (!addDisplayNameToFilter(record, builder, 'dis')) { - if (!addDisplayNameToFilter(record, builder, 'name')) { - if (!addDisplayNameToFilter(record, builder, 'tag')) { - addDisplayNameToFilter(record, builder, 'navName') + if (useDisplayName) { + // Build the display name match if one is available. + if (!addDisplayNameToFilter(record, builder, 'dis')) { + if (!addDisplayNameToFilter(record, builder, 'name')) { + if (!addDisplayNameToFilter(record, builder, 'tag')) { + addDisplayNameToFilter(record, builder, 'navName') + } } } } // Include a point's kind if available. - if (record.has('point') && record.has('kind')) { + if (useKind && record.has('point') && record.has('kind')) { const kind = record.get('kind')?.value if (kind) { if (!builder.isEmpty()) { From 07f7667cddccfe92ae33fb1eeb83404be5e61507 Mon Sep 17 00:00:00 2001 From: Gareth Johnson Date: Thu, 24 Apr 2025 16:28:43 +0100 Subject: [PATCH 3/3] Break point kind into separate function --- src/filter/util.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/filter/util.ts b/src/filter/util.ts index ea79966..1076d8f 100644 --- a/src/filter/util.ts +++ b/src/filter/util.ts @@ -20,7 +20,7 @@ export interface RelativizeOptions { useDisplayName?: boolean /** - * True (or undefined) if the point's kind should be used in the relativization. + * True (or undefined) if a point's kind should be used in the relativization. */ useKind?: boolean } @@ -62,15 +62,8 @@ export function makeRelativeHaystackFilter( } } - // Include a point's kind if available. if (useKind && record.has('point') && record.has('kind')) { - const kind = record.get('kind')?.value - if (kind) { - if (!builder.isEmpty()) { - builder.and() - } - builder.equals('kind', kind) - } + addPointKindToFilter(record, builder) } if (builder.isEmpty() && record.has('id')) { @@ -80,6 +73,16 @@ export function makeRelativeHaystackFilter( return builder.build() } +function addPointKindToFilter(record: HDict, builder: HFilterBuilder): void { + const kind = record.get('kind')?.value + if (kind) { + if (!builder.isEmpty()) { + builder.and() + } + builder.equals('kind', kind) + } +} + function addDisplayNameToFilter( record: HDict, builder: HFilterBuilder,