feat: Add LessThanOrNull and GreaterThanOrNull types with corresponding query helpers and examples

This commit is contained in:
Simon Larsen
2025-05-15 20:10:53 +01:00
parent 0bc9929949
commit 233bc1b70d
13 changed files with 164 additions and 1 deletions

View File

@@ -0,0 +1,8 @@
{
"query": {
"age": {
"_type": "GreaterThanOrNull",
"value": 10
}
}
}

View File

@@ -0,0 +1,8 @@
{
"query": {
"age": {
"_type": "LessThanOrNull",
"value": 10
}
}
}

View File

@@ -88,6 +88,26 @@ export default class ServiceHandler {
},
);
pageData.lessThanOrNullCode = await LocalCache.getOrSetString(
"data-type",
"less-than-or-equal",
async () => {
return await LocalFile.read(
`${CodeExamplesPath}/DataTypes/LessThanOrNull.md`,
);
},
);
pageData.greaterThanOrNullCode = await LocalCache.getOrSetString(
"data-type",
"less-than-or-equal",
async () => {
return await LocalFile.read(
`${CodeExamplesPath}/DataTypes/LessThanOrNull.md`,
);
},
);
pageData.isNullCode = await LocalCache.getOrSetString(
"data-type",
"is-null",

View File

@@ -3,10 +3,12 @@ import AnalyticsTableColumn from "../../../Types/AnalyticsDatabase/TableColumn";
import TableColumnType from "../../../Types/AnalyticsDatabase/TableColumnType";
import GreaterThan from "../../../Types/BaseDatabase/GreaterThan";
import GreaterThanOrEqual from "../../../Types/BaseDatabase/GreaterThanOrEqual";
import GreaterThanOrNull from "../../../Types/BaseDatabase/GreaterThanOrNull";
import InBetween from "../../../Types/BaseDatabase/InBetween";
import Includes from "../../../Types/BaseDatabase/Includes";
import LessThan from "../../../Types/BaseDatabase/LessThan";
import LessThanOrEqual from "../../../Types/BaseDatabase/LessThanOrEqual";
import LessThanOrNull from "../../../Types/BaseDatabase/LessThanOrNull";
import NotEqual from "../../../Types/BaseDatabase/NotEqual";
import Search from "../../../Types/BaseDatabase/Search";
import { CompareType } from "../../../Types/Database/CompareBase";
@@ -31,6 +33,8 @@ export type RecordValue =
| LessThan<CompareType>
| LessThanOrEqual<CompareType>
| GreaterThanOrEqual<CompareType>
| GreaterThanOrNull<CompareType>
| LessThanOrNull<CompareType>
| Array<number>
| Array<string>
| Array<ObjectID>

View File

@@ -313,6 +313,21 @@ export default class QueryHelper {
) as FindWhereProperty<T>;
}
@CaptureSpan()
public static lessThanOrNull<T extends number | Date>(
value: T,
): FindWhereProperty<T> {
const rid: string = Text.generateRandomText(10);
return Raw(
(alias: string) => {
return `(${alias} <= :${rid} or ${alias} IS NULL)`;
},
{
[rid]: value,
},
) as FindWhereProperty<T>;
}
@CaptureSpan()
public static lessThanEqualToOrNull<T extends number | Date>(
value: T,

View File

@@ -20,6 +20,8 @@ import Typeof from "Common/Types/Typeof";
import { FindOperator } from "typeorm/find-options/FindOperator";
import { CompareType } from "../../../Types/Database/CompareBase";
import CaptureSpan from "../../Utils/Telemetry/CaptureSpan";
import LessThanOrNull from "../../../Types/BaseDatabase/LessThanOrNull";
import GreaterThanOrNull from "../../../Types/BaseDatabase/GreaterThanOrNull";
export default class QueryUtil {
@CaptureSpan()
@@ -160,6 +162,22 @@ export default class QueryUtil {
query[key] = QueryHelper.lessThanEqualTo(
(query[key] as LessThanOrEqual<CompareType>).toString() as any,
) as any;
} else if (
query[key] &&
query[key] instanceof LessThanOrNull &&
tableColumnMetadata
) {
query[key] = QueryHelper.lessThanOrNull(
(query[key] as LessThanOrNull<CompareType>).toString() as any,
) as any;
} else if (
query[key] &&
query[key] instanceof GreaterThanOrNull &&
tableColumnMetadata
) {
query[key] = QueryHelper.greaterThanOrNull(
(query[key] as LessThanOrNull<CompareType>).toString() as any,
) as any;
} else if (
query[key] &&
Array.isArray(query[key]) &&

View File

@@ -7,6 +7,8 @@ import GreaterThanOrEqual from "Common/Types/BaseDatabase/GreaterThanOrEqual";
import Includes from "Common/Types/BaseDatabase/Includes";
import LessThan from "Common/Types/BaseDatabase/LessThan";
import LessThanOrEqual from "Common/Types/BaseDatabase/LessThanOrEqual";
import LessThanOrNull from "../../../Types/BaseDatabase/LessThanOrNull";
import GreaterThanOrNull from "../../../Types/BaseDatabase/GreaterThanOrNull";
import Search from "Common/Types/BaseDatabase/Search";
import OneUptimeDate from "Common/Types/Date";
import Dictionary from "Common/Types/Dictionary";
@@ -99,7 +101,9 @@ export class Statement implements BaseQueryParams {
v.value instanceof LessThan ||
v.value instanceof LessThanOrEqual ||
v.value instanceof GreaterThan ||
v.value instanceof GreaterThanOrEqual
v.value instanceof GreaterThanOrEqual ||
v.value instanceof LessThanOrNull ||
v.value instanceof GreaterThanOrNull
) {
finalValue = v.value.value;
} else if (v.value instanceof Includes) {

View File

@@ -20,6 +20,8 @@ import Includes from "Common/Types/BaseDatabase/Includes";
import IsNull from "Common/Types/BaseDatabase/IsNull";
import LessThan from "Common/Types/BaseDatabase/LessThan";
import LessThanOrEqual from "Common/Types/BaseDatabase/LessThanOrEqual";
import GreaterThanOrNull from "../../../Types/BaseDatabase/GreaterThanOrNull";
import LessThanOrNull from "../../../Types/BaseDatabase/LessThanOrNull";
import NotEqual from "Common/Types/BaseDatabase/NotEqual";
import Search from "Common/Types/BaseDatabase/Search";
import SortOrder from "Common/Types/BaseDatabase/SortOrder";
@@ -382,6 +384,20 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
type: tableColumn.type,
}}`,
);
} else if (value instanceof LessThanOrNull) {
whereStatement.append(
SQL`AND (${key} <= ${{
value: value,
type: tableColumn.type,
}} OR ${key} IS NULL)`,
);
}else if (value instanceof GreaterThanOrNull) {
whereStatement.append(
SQL`AND (${key} >= ${{
value: value,
type: tableColumn.type,
}} OR ${key} IS NULL)`,
);
} else if (value instanceof GreaterThanOrEqual) {
whereStatement.append(
SQL`AND ${key} >= ${{

View File

@@ -19,6 +19,8 @@ describe("ObjectType", () => {
"GreaterThanOrEqual",
"LessThan",
"LessThanOrEqual",
"LessThanOrNull",
"GreaterThanOrNull",
"Port",
"Hostname",
"HashedString",

View File

@@ -0,0 +1,28 @@
import CompareBase, { CompareType } from "../Database/CompareBase";
import BadDataException from "../Exception/BadDataException";
import { JSONObject, ObjectType } from "../JSON";
export default class GreaterThanOrNull<
T extends CompareType,
> extends CompareBase<T> {
public constructor(value: T) {
super(value);
}
public override toJSON(): JSONObject {
return {
_type: ObjectType.GreaterThanOrNull,
value: (this as GreaterThanOrNull<T>).toString(),
};
}
public static override fromJSON<T extends CompareType>(
json: JSONObject,
): GreaterThanOrNull<T> {
if (json["_type"] === ObjectType.GreaterThanOrNull) {
return new GreaterThanOrNull<T>(json["value"] as T);
}
throw new BadDataException("Invalid JSON: " + JSON.stringify(json));
}
}

View File

@@ -0,0 +1,28 @@
import CompareBase, { CompareType } from "../Database/CompareBase";
import BadDataException from "../Exception/BadDataException";
import { JSONObject, ObjectType } from "../JSON";
export default class LessThanOrNull<
T extends CompareType,
> extends CompareBase<T> {
public constructor(value: T) {
super(value);
}
public override toJSON(): JSONObject {
return {
_type: ObjectType.LessThanOrNull,
value: (this as LessThanOrNull<T>).toString(),
};
}
public static override fromJSON<T extends CompareType>(
json: JSONObject,
): LessThanOrNull<T> {
if (json["_type"] === ObjectType.LessThanOrNull) {
return new LessThanOrNull<T>(json["value"] as T);
}
throw new BadDataException("Invalid JSON: " + JSON.stringify(json));
}
}

View File

@@ -28,6 +28,8 @@ import StartAndEndTime from "./Time/StartAndEndTime";
import Version from "./Version";
import { BaseEntity } from "typeorm";
import DashboardViewConfig from "./Dashboard/DashboardViewConfig";
import LessThanOrNull from "./BaseDatabase/LessThanOrNull";
import GreaterThanOrNull from "./BaseDatabase/GreaterThanOrNull";
export enum ObjectType {
ObjectID = "ObjectID",
@@ -54,6 +56,8 @@ export enum ObjectType {
Search = "Search",
GreaterThan = "GreaterThan",
GreaterThanOrEqual = "GreaterThanOrEqual",
GreaterThanOrNull = "GreaterThanOrNull",
LessThanOrNull = "LessThanOrNull",
LessThan = "LessThan",
LessThanOrEqual = "LessThanOrEqual",
Port = "Port",
@@ -119,6 +123,10 @@ export type JSONValue =
| Array<GreaterThan<CompareType>>
| GreaterThanOrEqual<CompareType>
| Array<GreaterThanOrEqual<CompareType>>
| LessThanOrNull<CompareType>
| Array<LessThanOrNull<CompareType>>
| GreaterThanOrNull <CompareType>
| Array<GreaterThanOrNull<CompareType>>
| PositiveNumber
| Array<PositiveNumber>
| LessThan<CompareType>

View File

@@ -9,6 +9,8 @@ import Includes from "./BaseDatabase/Includes";
import IsNull from "./BaseDatabase/IsNull";
import LessThan from "./BaseDatabase/LessThan";
import LessThanOrEqual from "./BaseDatabase/LessThanOrEqual";
import LessThanOrNull from "./BaseDatabase/LessThanOrNull";
import GreaterThanOrNull from "./BaseDatabase/GreaterThanOrNull";
import NotEqual from "./BaseDatabase/NotEqual";
import NotNull from "./BaseDatabase/NotNull";
import Search from "./BaseDatabase/Search";
@@ -54,6 +56,8 @@ const SerializableObjectDictionary: Dictionary<any> = {
[ObjectType.GreaterThanOrEqual]: GreaterThanOrEqual,
[ObjectType.LessThan]: LessThan,
[ObjectType.LessThanOrEqual]: LessThanOrEqual,
[ObjectType.LessThanOrNull]: LessThanOrNull,
[ObjectType.GreaterThanOrNull]: GreaterThanOrNull,
[ObjectType.Port]: Port,
[ObjectType.Hostname]: Hostname,
[ObjectType.HashedString]: HashedString,