Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add inbetween tests + fix fragment ordering issue with inbetwee… #45

Merged
merged 2 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 32 additions & 268 deletions lib/condition/condition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { NamedNode, Parser } from "n3";
import { BasicLensM, Cont, extractShapes } from "rdf-lens";
import { RdfStore } from "rdf-stores";
import { Member } from "../page";
import { TREE, XSD } from "@treecg/types";
import { TREE } from "@treecg/types";
import { SHAPES } from "./shapes";
import { cbdEquals, Path } from "./range";
import { getLoggerFor } from "../utils/logUtil";
import { parseInBetweenRelation } from "../utils/inBetween";

type RdfThing = {
entry: Term;
Expand Down Expand Up @@ -98,40 +99,26 @@ export class Range {
case TREE.GreaterThanOrEqualToRelation:
this.min = value;
return;
case TREE.custom("InBetweenRelation"):
case TREE.custom("InBetweenRelation"): {
if (typeof value !== "string") {
throw (
"InBetweenRelation can only handle string values, not" +
typeof value
);
}
if (dataType === XSD.custom("gYear")) {
const result = this.gYearToMinMax(value);
if (!result) return;
[this.min, this.max] = result;
this.eqMin = true;
this.eqMax = false;
} else if (dataType === XSD.custom("gYearMonth")) {
const result = this.gYearMonthToMinMax(value);
if (!result) return;
[this.min, this.max] = result;
this.eqMin = true;
this.eqMax = false;
} else if (dataType === XSD.custom("date")) {
const result = this.dateToMinMax(value);
if (!result) return;
[this.min, this.max] = result;
this.eqMin = true;
this.eqMax = false;
} else {
// Check if it is a partial dateTime.
const result = this.partialDateTimeToMinMax(value);
if (!result) return;
[this.min, this.max] = result;
const between = parseInBetweenRelation(
value,
dataType,
this.defaultTimezone,
);
if (between) {
this.min = between.min;
this.max = between.max;
this.eqMin = true;
this.eqMax = false;
}
return;
}
}
}

Expand Down Expand Up @@ -167,58 +154,20 @@ export class Range {
this.min = value;
}
return;
case TREE.custom("InBetweenRelation"):
if (dataType === XSD.custom("gYear")) {
const result = this.gYearToMinMax(value);
if (!result) return;
const [min, max] = result;
if (!this.min || min <= this.min) {
this.min = min;
this.eqMin = true;
}
if (!this.max || max > this.max) {
this.max = max;
this.eqMax = false;
}
} else if (dataType === XSD.custom("gYearMonth")) {
const result = this.gYearMonthToMinMax(value);
if (!result) return;
const [min, max] = result;
if (!this.min || min <= this.min) {
this.min = min;
this.eqMin = true;
}
if (!this.max || max > this.max) {
this.max = max;
this.eqMax = false;
}
} else if (dataType === XSD.custom("date")) {
const result = this.dateToMinMax(value);
if (!result) return;
const [min, max] = result;
if (!this.min || min <= this.min) {
this.min = min;
this.eqMin = true;
}
if (!this.max || max > this.max) {
this.max = max;
this.eqMax = false;
}
} else {
// Check if it is a partial dateTime
const result = this.partialDateTimeToMinMax(value);
if (!result) return;
const [min, max] = result;
if (!this.min || min <= this.min) {
this.min = min;
this.eqMin = true;
}
if (!this.max || max > this.max) {
this.max = max;
this.eqMax = false;
}
case TREE.custom("InBetweenRelation"): {
const between = parseInBetweenRelation(
value,
dataType,
this.defaultTimezone,
);
if (between) {
this.min = between.min;
this.eqMin = true;
this.max = between.max;
this.eqMax = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you don't do the detailed assignments. So this should go to the constructor, and the change from the constructor should go here.

}
return;
}
}
}

Expand Down Expand Up @@ -267,201 +216,11 @@ export class Range {
const end = this.max ? vts(this.max) + (this.eqMax ? "]" : ")") : "[";
return start + comma + end;
}

private gYearToMinMax(value: string): [Date, Date] | undefined {
const regex =
/^(-?[1-9][0-9]{3,}|-?0[0-9]{3})(Z|(\+|-)((0[0-9]|1[0-3]):([0-5][0-9])|14:00))?$/;
const match = value.match(regex);
if (!match) {
this.logger.warn(`Invalid gYear format: ${value}`);
return;
}
const year = parseInt(match[1]);
let minOffset = 0;
let maxOffset = 0;
const timezone = match[2] || this.defaultTimezone;
if (timezone === "AoE") {
// Anywhere on Earth approach.
minOffset = -12 * 60;
maxOffset = 12 * 60;
} else if (timezone !== "Z") {
const sign = match[3];
const h = parseInt(match[5]);
const m = parseInt(match[6]);
const offset = (sign === "+" ? 1 : -1) * (h * 60 + m);
minOffset = offset;
maxOffset = offset;
}
return [
new Date(Date.UTC(year, 0, 1) + minOffset * 60 * 1000),
new Date(Date.UTC(year + 1, 0, 1) + maxOffset * 60 * 1000),
];
}

private gYearMonthToMinMax(value: string): [Date, Date] | undefined {
const regex =
/^(-?[1-9][0-9]{3,}|-?0[0-9]{3})-(0[1-9]|1[0-2])(Z|(\+|-)((0[0-9]|1[0-3]):([0-5][0-9])|14:00))?$/;
const match = value.match(regex);
if (!match) {
this.logger.warn(`Invalid gYearMonth format: ${value}`);
return;
}
const y = parseInt(match[1]);
const m = parseInt(match[2]);
let minOffset = 0;
let maxOffset = 0;
const timezone = match[3] || this.defaultTimezone;
if (timezone === "AoE") {
// Anywhere on Earth approach.
minOffset = -12 * 60;
maxOffset = 12 * 60;
} else if (timezone !== "Z") {
const sign = match[4];
const h = parseInt(match[6]);
const min = parseInt(match[7]);
const offset = (sign === "+" ? 1 : -1) * (h * 60 + min);
minOffset = offset;
maxOffset = offset;
}
return [
new Date(Date.UTC(y, m - 1, 1) + minOffset * 60 * 1000),
new Date(Date.UTC(y, m, 1) + maxOffset * 60 * 1000),
];
}

private dateToMinMax(value: string): [Date, Date] | undefined {
const regex =
/^(-?[1-9][0-9]{3,}|-?0[0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])(Z|(\+|-)((0[0-9]|1[0-3]):([0-5][0-9])|14:00))?$/;
const match = value.match(regex);
if (!match) {
this.logger.warn(`Invalid date format: ${value}`);
return;
}
const y = parseInt(match[1]);
const m = parseInt(match[2]);
const d = parseInt(match[3]);
let minOffset = 0;
let maxOffset = 0;
const timezone = match[4] || this.defaultTimezone;
if (timezone === "AoE") {
// Anywhere on Earth approach.
minOffset = -12 * 60;
maxOffset = 12 * 60;
} else if (timezone !== "Z") {
const sign = match[5];
const h = parseInt(match[7]);
const min = parseInt(match[8]);
const offset = (sign === "+" ? 1 : -1) * (h * 60 + min);
minOffset = offset;
maxOffset = offset;
}
return [
new Date(Date.UTC(y, m - 1, d) + minOffset * 60 * 1000),
new Date(Date.UTC(y, m - 1, d + 1) + maxOffset * 60 * 1000),
];
}

private partialDateTimeToMinMax(value: string): [Date, Date] | undefined {
const dateHourMinSecRegex =
/^(-?[1-9][0-9]{3,}|-?0[0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(Z|(\+|-)((0[0-9]|1[0-3]):([0-5][0-9])|14:00))?$/;
const matchDHMS = value.match(dateHourMinSecRegex);
if (matchDHMS) {
const y = parseInt(matchDHMS[1]);
const m = parseInt(matchDHMS[2]);
const d = parseInt(matchDHMS[3]);
const h = parseInt(matchDHMS[4]);
const min = parseInt(matchDHMS[5]);
const s = parseInt(matchDHMS[6]);
let minOffset = 0;
let maxOffset = 0;
const timezone = matchDHMS[7] || this.defaultTimezone;
if (timezone === "AoE") {
// Anywhere on Earth approach.
minOffset = -12 * 60;
maxOffset = 12 * 60;
} else if (timezone !== "Z") {
const sign = matchDHMS[8];
const hOff = parseInt(matchDHMS[10]);
const minOff = parseInt(matchDHMS[11]);
const offset = (sign === "+" ? 1 : -1) * (hOff * 60 + minOff);
minOffset = offset;
maxOffset = offset;
}
return [
new Date(
Date.UTC(y, m - 1, d, h, min, s) + minOffset * 60 * 1000,
),
new Date(
Date.UTC(y, m - 1, d, h, min, s + 1) +
maxOffset * 60 * 1000,
),
];
}
const dateHourMinRegex =
/^(-?[1-9][0-9]{3,}|-?0[0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9])(Z|(\+|-)((0[0-9]|1[0-3]):([0-5][0-9])|14:00))?$/;
const matchDHM = value.match(dateHourMinRegex);
if (matchDHM) {
const y = parseInt(matchDHM[1]);
const m = parseInt(matchDHM[2]);
const d = parseInt(matchDHM[3]);
const h = parseInt(matchDHM[4]);
const min = parseInt(matchDHM[5]);
let minOffset = 0;
let maxOffset = 0;
const timezone = matchDHM[6] || this.defaultTimezone;
if (timezone === "AoE") {
// Anywhere on Earth approach.
minOffset = -12 * 60;
maxOffset = 12 * 60;
} else if (timezone !== "Z") {
const sign = matchDHM[7];
const hOff = parseInt(matchDHM[9]);
const minOff = parseInt(matchDHM[10]);
const offset = (sign === "+" ? 1 : -1) * (hOff * 60 + minOff);
minOffset = offset;
maxOffset = offset;
}
return [
new Date(Date.UTC(y, m - 1, d, h, min) + minOffset * 60 * 1000),
new Date(
Date.UTC(y, m - 1, d, h, min + 1) + maxOffset * 60 * 1000,
),
];
}
const dateHourRegex =
/^(-?[1-9][0-9]{3,}|-?0[0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3])(Z|(\+|-)((0[0-9]|1[0-3]):([0-5][0-9])|14:00))?$/;
const matchDH = value.match(dateHourRegex);
if (matchDH) {
const y = parseInt(matchDH[1]);
const m = parseInt(matchDH[2]);
const d = parseInt(matchDH[3]);
const h = parseInt(matchDH[4]);
let minOffset = 0;
let maxOffset = 0;
const timezone = matchDH[5] || this.defaultTimezone;
if (timezone === "AoE") {
// Anywhere on Earth approach.
minOffset = -12 * 60;
maxOffset = 12 * 60;
} else if (timezone !== "Z") {
const sign = matchDH[6];
const hOff = parseInt(matchDH[8]);
const minOff = parseInt(matchDH[9]);
const offset = (sign === "+" ? 1 : -1) * (hOff * 60 + minOff);
minOffset = offset;
maxOffset = offset;
}
return [
new Date(Date.UTC(y, m - 1, d, h) + minOffset * 60 * 1000),
new Date(Date.UTC(y, m - 1, d, h + 1) + maxOffset * 60 * 1000),
];
}
}
}

export class LeafCondition implements Condition {
relationType: Term;
value: string;
value: Term;
compareType: CompareTypes;

path: BasicLensM<Cont, Cont>;
Expand All @@ -475,7 +234,7 @@ export class LeafCondition implements Condition {

constructor(inp: {
relationType: Term;
value: string;
value: Term;
compareType?: string;
path: BasicLensM<Cont, Cont>;
pathQuads: RdfThing;
Expand All @@ -489,11 +248,16 @@ export class LeafCondition implements Condition {
inp.pathQuads.quads.forEach((x) => store.addQuad(x));
this.pathQuads = { id: inp.pathQuads.entry, store };
this.defaultTimezone = inp.defaultTimezone;
let dataType;
if (inp.value.termType === "Literal") {
dataType = inp.value.datatype.value;
}

this.range = new Range(
this.parseValue(inp.value),
this.parseValue(inp.value.value),
inp.relationType.value,
this.defaultTimezone,
dataType,
);
}

Expand Down
10 changes: 8 additions & 2 deletions lib/condition/range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ export function cbdEquals(a: Path, b: Path): boolean {
if (alphaQuads.length != betaQuads.length) return false;

for (let i = 0; i < alphaQuads.length; i++) {
if (!alphaQuads[i].predicate.equals(betaQuads[i].predicate))
if (!alphaQuads[i].predicate.equals(betaQuads[i].predicate)) {
return false;
}

const av = alphaQuads[i].object;
const bv = betaQuads[i].object;
Expand Down Expand Up @@ -114,6 +115,11 @@ export class RelationCondition {
return;
}

range.range?.add(value.value, ty.value);
let dataType;
if (value.termType === "Literal") {
dataType = value.datatype.value;
}

range.range?.add(value.value, ty.value, dataType);
ajuvercr marked this conversation as resolved.
Show resolved Hide resolved
}
}
2 changes: 1 addition & 1 deletion lib/condition/shapes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const SHAPES = `
sh:name "path";
], [
sh:path tree:value;
sh:datatype xsd:string;
sh:datatype xsd:iri;
sh:maxCount 1;
sh:name "value";
], [
Expand Down
Loading
Loading