From 47b7ce323c21b71a35e05e85029b2bd2e871adcf Mon Sep 17 00:00:00 2001 From: Ovais Tariq Date: Wed, 17 May 2023 16:28:59 -0700 Subject: [PATCH] fix: Revert "feat: static type binding for query" (#373) Revert "fix: Revert "feat: static type binding for query" (#371)" This reverts commit f348df246d910664b4113e3befbec82647dd1644. --- src/__tests__/tigris.readfields.spec.ts | 31 +++++++++++++----- src/__tests__/tigris.utility.spec.ts | 28 ++++++++++++----- src/collection.ts | 6 ++-- src/search/query.ts | 20 ++++++------ src/types.ts | 42 +++++++++++++------------ src/utility.ts | 13 +++++--- 6 files changed, 87 insertions(+), 53 deletions(-) diff --git a/src/__tests__/tigris.readfields.spec.ts b/src/__tests__/tigris.readfields.spec.ts index 3e55962..70a20c9 100644 --- a/src/__tests__/tigris.readfields.spec.ts +++ b/src/__tests__/tigris.readfields.spec.ts @@ -1,24 +1,39 @@ -import { ReadFields } from "../types"; +import { ReadFields, TigrisCollectionType } from "../types"; import { Utility } from "../utility"; +export interface IBook1 extends TigrisCollectionType { + id?: number; + title: string; + author: Author; + tags?: string[]; +} + +export interface Author extends TigrisCollectionType { + firstName: string; + lastName: string; +} describe("readFields tests", () => { it("readFields1", () => { - const readFields: ReadFields = { - include: ["id", "title"], + const readFields: ReadFields = { + include: ["id", "title", "author.firstName", "author.lastName"], }; - expect(Utility.readFieldString(readFields)).toBe('{"id":true,"title":true}'); + expect(Utility.readFieldString(readFields)).toBe( + '{"id":true,"title":true,"author.firstName":true,"author.lastName":true}' + ); }); it("readFields2", () => { - const readFields: ReadFields = { + const readFields: ReadFields = { exclude: ["id", "title"], }; - expect(Utility.readFieldString(readFields)).toBe('{"id":false,"title":false}'); + expect(Utility.readFieldString(readFields)).toBe('{"id":false,"title":false}'); }); it("readFields3", () => { - const readFields: ReadFields = { + const readFields: ReadFields = { include: ["id", "title"], exclude: ["author"], }; - expect(Utility.readFieldString(readFields)).toBe('{"id":true,"title":true,"author":false}'); + expect(Utility.readFieldString(readFields)).toBe( + '{"id":true,"title":true,"author":false}' + ); }); }); diff --git a/src/__tests__/tigris.utility.spec.ts b/src/__tests__/tigris.utility.spec.ts index 91297d7..54a8097 100644 --- a/src/__tests__/tigris.utility.spec.ts +++ b/src/__tests__/tigris.utility.spec.ts @@ -9,11 +9,24 @@ import { SearchQueryOptions, } from "../search"; import { SearchRequest as ProtoSearchRequest } from "../proto/server/v1/api_pb"; -import { SortOrder } from "../types"; +import { SortOrder, TigrisCollectionType } from "../types"; import { Field } from "../decorators/tigris-field"; import { TigrisCollection } from "../decorators/tigris-collection"; import { PrimaryKey } from "../decorators/tigris-primary-key"; +interface ICollectionFields extends TigrisCollectionType { + field_1: string; + field_2: string; + field_3: string; + parent?: IParent; +} + +interface IParent extends TigrisCollectionType { + field_1?: string; + field_2?: string; + field_3?: string; +} + describe("utility tests", () => { it("base64encode", () => { expect(Utility._base64Encode("hello world")).toBe("aGVsbG8gd29ybGQ="); @@ -40,7 +53,7 @@ describe("utility tests", () => { }); it("serializes FacetFields to string", () => { - const fields: FacetFields = ["field_1", "field_2"]; + const fields: FacetFields = ["field_1", "field_2"]; const serialized: string = Utility.facetQueryToString(fields); expect(serialized).toBe( '{"field_1":{"size":10,"type":"value"},"field_2":{"size":10,"type":"value"}}' @@ -48,7 +61,7 @@ describe("utility tests", () => { }); it("serializes FacetFieldOptions to string", () => { - const fields: FacetFieldOptions = { + const fields: FacetFieldOptions = { field_1: Utility.defaultFacetingOptions(), field_2: { size: 10 }, }; @@ -59,8 +72,8 @@ describe("utility tests", () => { }); it("equivalent serialization of FacetFieldsQuery", () => { - const facetFields: FacetFieldsQuery = ["field_1", "field_2"]; - const fieldOptions: FacetFieldsQuery = { + const facetFields: FacetFieldsQuery = ["field_1", "field_2"]; + const fieldOptions: FacetFieldsQuery = { field_1: Utility.defaultFacetingOptions(), field_2: { size: 10, type: "value" }, }; @@ -68,8 +81,7 @@ describe("utility tests", () => { expect(serializedFields).toBe(Utility.facetQueryToString(fieldOptions)); }); - it.each<[string, SortOrder, string]>([ - ["undefined", undefined, "[]"], + it.each<[string, SortOrder, string]>([ [ "multiple sort fields", [ @@ -81,7 +93,7 @@ describe("utility tests", () => { ["single sort field", { field: "field_3", order: "$desc" }, '[{"field_3":"$desc"}]'], ["empty array", [], "[]"], ])("_sortOrderingToString() with '%s'", (testName, input, expected) => { - expect(Utility._sortOrderingToString(input)).toBe(expected); + expect(Utility._sortOrderingToString(input)).toBe(expected); }); describe("createProtoSearchRequest", () => { diff --git a/src/collection.ts b/src/collection.ts index 6177af6..c1266d8 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -569,11 +569,13 @@ export class Collection implements ICollection { .setFilter(Utility.stringToUint8Array(Utility.filterToString(query.filter))); if (query.readFields) { - readRequest.setFields(Utility.stringToUint8Array(Utility.readFieldString(query.readFields))); + readRequest.setFields( + Utility.stringToUint8Array(Utility.readFieldString(query.readFields)) + ); } if (query.sort) { - readRequest.setSort(Utility.stringToUint8Array(Utility._sortOrderingToString(query.sort))); + readRequest.setSort(Utility.stringToUint8Array(Utility._sortOrderingToString(query.sort))); } if (query.options) { diff --git a/src/search/query.ts b/src/search/query.ts index 33f0cb6..ee3f0e6 100644 --- a/src/search/query.ts +++ b/src/search/query.ts @@ -1,4 +1,4 @@ -import { Filter, SortOrder, TigrisCollectionType } from "../types"; +import { DocumentPaths, Filter, SortOrder, TigrisCollectionType } from "../types"; import { TigrisIndexType } from "./types"; export const MATCH_ALL_QUERY_STRING = ""; @@ -14,7 +14,7 @@ export interface SearchQuery { /** * Fields to project search query on */ - searchFields?: Array; + searchFields?: Array>; /** * Filter to further refine the search results */ @@ -22,7 +22,7 @@ export interface SearchQuery { /** * Facet fields to categorically arrange indexed terms */ - facets?: FacetFieldsQuery; + facets?: FacetFieldsQuery; /** * Perform a nearest neighbor search to find closest documents */ @@ -30,7 +30,7 @@ export interface SearchQuery { /** * Sort the search results in indicated order */ - sort?: SortOrder; + sort?: SortOrder; /** * Group by single or multiple fields in the index */ @@ -38,11 +38,11 @@ export interface SearchQuery { /** * Document fields to include when returning search results */ - includeFields?: Array; + includeFields?: Array>; /** * Document fields to exclude when returning search results */ - excludeFields?: Array; + excludeFields?: Array>; /** * Maximum number of search hits (matched documents) to fetch per page */ @@ -63,19 +63,19 @@ export interface SearchQueryOptions { collation?: Collation; } -export type FacetFieldsQuery = FacetFieldOptions | FacetFields; +export type FacetFieldsQuery = FacetFieldOptions | FacetFields; /** * Map of collection field names and faceting options to include facet results in search response */ -export type FacetFieldOptions = { - [key: string]: FacetQueryOptions; +export type FacetFieldOptions = { + [K in DocumentPaths]?: FacetQueryOptions; }; /** * Array of field names to include facet results for in search response */ -export type FacetFields = Array; +export type FacetFields = Array>; /** * Information to build facets in search results diff --git a/src/types.ts b/src/types.ts index a62958f..cbdd810 100644 --- a/src/types.ts +++ b/src/types.ts @@ -494,9 +494,9 @@ export interface TigrisCollectionType { export type NumericType = number | bigint; export type FieldTypes = string | boolean | NumericType | BigInteger | Date | object; -export type ReadFields = { - include?: Array; - exclude?: Array; +export type ReadFields = { + include?: Array>; + exclude?: Array>; }; type DocumentFields = Partial<{ @@ -518,13 +518,13 @@ export type UpdateFields = /** * List of fields and their corresponding sort order to order the search results. */ -export type SortOrder = SortField | Array; +export type SortOrder = SortField | Array>; /** * Collection field name and sort order */ -export type SortField = { - field: string; +export type SortField = { + field: DocumentPaths; order: "$asc" | "$desc"; }; @@ -549,12 +549,12 @@ export interface FindQuery { * Field projection to allow returning only specific document fields. By default * all document fields are returned. */ - readFields?: ReadFields; + readFields?: ReadFields; /** * Sort the query results as per indicated order */ - sort?: SortOrder; + sort?: SortOrder; /** * Optional params @@ -751,17 +751,19 @@ export type Selector = Partial<{ [K in string]: unknown; }>; -type PathsForFilter = { - [K in keyof T]: T[K] extends object - ? T[K] extends unknown[] - ? `${P}${K & string}` - : Paths extends infer O - ? T[K] extends Date | BigInt - ? `${O & string}` | `${P}${K & string}` - : `${O & string}` - : never - : `${P}${K & string}`; -}[keyof T]; +/** + * Compute all possible property combinations + */ +type normalTypes = PropertyKey | BigInt | Date | boolean | Array; +export type DocumentPaths = T extends normalTypes + ? Cache + : { + [P in keyof T]: P extends string + ? Cache extends "" + ? DocumentPaths + : Cache | DocumentPaths + : `${Cache}${P & string}`; + }[keyof T]; export type SelectorOperator = | "$eq" @@ -776,7 +778,7 @@ export type SelectorOperator = export type LogicalOperator = "$or" | "$and"; export type SelectorFilter = { - [K in PathsForFilter]?: PathType | { [P in SelectorOperator]?: PathType }; + [K in DocumentPaths]?: PathType | { [P in SelectorOperator]?: PathType }; }; export type LogicalFilter = { diff --git a/src/utility.ts b/src/utility.ts index b1c4c00..a31b558 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -87,7 +87,7 @@ export const Utility = { _getRandomInt(upperBound: number): number { return Math.floor(Math.random() * upperBound); }, - readFieldString(readFields: ReadFields): string { + readFieldString(readFields: ReadFields): string { const include = readFields.include?.reduce((acc, field) => ({ ...acc, [field]: true }), {}); const exclude = readFields.exclude?.reduce((acc, field) => ({ ...acc, [field]: false }), {}); @@ -491,8 +491,9 @@ export const Utility = { return { ...defaults, ...options }; }, - facetQueryToString(facets: FacetFieldsQuery): string { - const optionsMap = {}; + facetQueryToString(facets: FacetFieldsQuery): string { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const optionsMap: any = {}; if (Array.isArray(facets)) { for (const f of facets) { optionsMap[f] = this.defaultFacetingOptions(); @@ -512,7 +513,7 @@ export const Utility = { return this.objToJsonString(q); }, - _sortOrderingToString(ordering: SortOrder): string { + _sortOrderingToString(ordering: SortOrder): string { if (typeof ordering === "undefined") { return "[]"; } @@ -567,7 +568,9 @@ export const Utility = { } if (query.sort !== undefined) { - searchRequest.setSort(Utility.stringToUint8Array(Utility._sortOrderingToString(query.sort))); + searchRequest.setSort( + Utility.stringToUint8Array(Utility._sortOrderingToString(query.sort)) + ); } if (query.groupBy !== undefined) {