@tanstack/db
Version:
A reactive client store for building super fast apps on sync
186 lines (185 loc) • 5.54 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const d2mini = require("@electric-sql/d2mini");
const extractors = require("./extractors.cjs");
const utils = require("./utils.cjs");
function processOrderBy(resultPipeline, query, mainTableAlias) {
let hasOrderIndexColumn = false;
let orderIndexType = `numeric`;
let orderIndexAlias = ``;
for (const item of query.select) {
if (typeof item === `object`) {
for (const [alias, expr] of Object.entries(item)) {
if (typeof expr === `object` && utils.isOrderIndexFunctionCall(expr)) {
hasOrderIndexColumn = true;
orderIndexAlias = alias;
orderIndexType = getOrderIndexType(expr);
break;
}
}
}
if (hasOrderIndexColumn) break;
}
const orderByItems = [];
if (typeof query.orderBy === `string`) {
orderByItems.push({
operand: query.orderBy,
direction: `asc`
});
} else if (Array.isArray(query.orderBy)) {
for (const item of query.orderBy) {
if (typeof item === `string`) {
orderByItems.push({
operand: item,
direction: `asc`
});
} else if (typeof item === `object`) {
for (const [column, direction] of Object.entries(item)) {
orderByItems.push({
operand: column,
direction
});
}
}
}
} else if (typeof query.orderBy === `object`) {
for (const [column, direction] of Object.entries(query.orderBy)) {
orderByItems.push({
operand: column,
direction
});
}
}
const valueExtractor = (namespacedRow) => {
if (orderByItems.length > 1) {
return orderByItems.map(
(item) => extractors.evaluateOperandOnNamespacedRow(
namespacedRow,
item.operand,
mainTableAlias
)
);
} else if (orderByItems.length === 1) {
const item = orderByItems[0];
const val = extractors.evaluateOperandOnNamespacedRow(
namespacedRow,
item.operand,
mainTableAlias
);
return val;
}
return null;
};
const ascComparator = (a, b) => {
if (typeof a === `string` && typeof b === `string`) {
return a.localeCompare(b);
}
if (Array.isArray(a) && Array.isArray(b)) {
for (let i = 0; i < Math.min(a.length, b.length); i++) {
const result = ascComparator(a[i], b[i]);
if (result !== 0) {
return result;
}
}
return a.length - b.length;
}
const bothObjects = typeof a === `object` && typeof b === `object`;
const bothDates = a instanceof Date && b instanceof Date;
const notNull = a !== null && b !== null;
if (bothObjects && !bothDates && notNull) {
return a.toString().localeCompare(b.toString());
}
if (a < b) return -1;
if (a > b) return 1;
return 0;
};
const descComparator = (a, b) => {
return ascComparator(b, a);
};
const makeComparator = (orderByProps) => {
return (a, b) => {
if (orderByProps.length > 1) {
const arrayA = a;
const arrayB = b;
for (let i = 0; i < orderByProps.length; i++) {
const direction = orderByProps[i].direction;
const compareFn = direction === `desc` ? descComparator : ascComparator;
const result = compareFn(arrayA[i], arrayB[i]);
if (result !== 0) {
return result;
}
}
return arrayA.length - arrayB.length;
}
if (orderByProps.length === 1) {
const direction = orderByProps[0].direction;
return direction === `desc` ? descComparator(a, b) : ascComparator(a, b);
}
return ascComparator(a, b);
};
};
const comparator = makeComparator(orderByItems);
if (hasOrderIndexColumn) {
if (orderIndexType === `numeric`) {
resultPipeline = resultPipeline.pipe(
d2mini.orderByWithIndex(valueExtractor, {
limit: query.limit,
offset: query.offset,
comparator
}),
d2mini.map(([key, [value, index]]) => {
const result = {
...value,
[mainTableAlias]: {
...value[mainTableAlias],
[orderIndexAlias]: index
}
};
return [key, result];
})
);
} else {
resultPipeline = resultPipeline.pipe(
d2mini.orderByWithFractionalIndex(valueExtractor, {
limit: query.limit,
offset: query.offset,
comparator
}),
d2mini.map(([key, [value, index]]) => {
const result = {
...value,
[mainTableAlias]: {
...value[mainTableAlias],
[orderIndexAlias]: index
}
};
return [key, result];
})
);
}
} else {
resultPipeline = resultPipeline.pipe(
d2mini.orderBy(valueExtractor, {
limit: query.limit,
offset: query.offset,
comparator
})
);
}
return resultPipeline;
}
function getOrderIndexType(obj) {
if (!utils.isOrderIndexFunctionCall(obj)) {
throw new Error(`Not an ORDER_INDEX function call`);
}
const arg = obj[`ORDER_INDEX`];
if (arg === `numeric` || arg === true || arg === `default`) {
return `numeric`;
} else if (arg === `fractional`) {
return `fractional`;
} else {
throw new Error(`Invalid ORDER_INDEX type: ` + arg);
}
}
exports.processOrderBy = processOrderBy;
//# sourceMappingURL=order-by.cjs.map