@algochad/prisma-core
Version:
A comprehensive NestJS library that provides EF-Core-like operations using Prisma and GraphQL. Features LINQ-style query builders, advanced data manipulation, GraphQL integration with genql, and a unified API for both Prisma and GraphQL operations. Includ
835 lines • 28.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InMemoryQueryable = exports.PrismaOrderedQueryable = exports.PrismaQueryable = exports.PrismaQueryProvider = void 0;
const async_enumerable_1 = require("../linq/async-enumerable");
const enumerable_1 = require("../linq/enumerable");
const list_1 = require("../linq/list");
class PrismaQueryProvider {
prismaModel;
modelName;
constructor(prismaModel, modelName) {
this.prismaModel = prismaModel;
this.modelName = modelName || 'unknown';
}
async Execute(expression) {
const prismaQuery = {};
if (expression.where) {
prismaQuery.where = expression.where;
}
if (expression.select) {
prismaQuery.select = expression.select;
}
if (expression.include) {
prismaQuery.include = expression.include;
}
if (expression.orderBy && expression.orderBy.length > 0) {
prismaQuery.orderBy = expression.orderBy;
}
if (expression.take !== undefined) {
prismaQuery.take = expression.take;
}
if (expression.skip !== undefined) {
prismaQuery.skip = expression.skip;
}
let results = await this.prismaModel.findMany(prismaQuery);
if (expression.inMemoryPredicates &&
expression.inMemoryPredicates.length > 0) {
for (const predicate of expression.inMemoryPredicates) {
if (predicate) {
results = results.filter(predicate);
}
}
}
return results;
}
CreateQuery(expression) {
return new PrismaQueryable(this, expression);
}
}
exports.PrismaQueryProvider = PrismaQueryProvider;
class PrismaQueryable {
provider;
expression;
elementType;
linqContext;
constructor(provider, expression, elementType, linqContext) {
this.provider = provider;
this.expression = expression || {};
this.elementType = elementType;
this.linqContext = linqContext;
}
get Provider() {
return this.provider;
}
get Expression() {
return this.expression;
}
get ElementType() {
return this.elementType;
}
createNew(newExpression) {
const mergedExpression = { ...this.expression, ...newExpression };
return new PrismaQueryable(this.provider, mergedExpression, this.elementType, this.linqContext);
}
WhereFilter(where) {
const existingWhere = this.expression.where;
let finalWhere;
if (existingWhere &&
typeof existingWhere === 'object' &&
typeof where === 'object') {
finalWhere = { AND: [existingWhere, where] };
}
else {
finalWhere = where;
}
return this.createNew({ where: finalWhere });
}
SelectFields(select) {
return this.createNew({ select });
}
Include(include) {
const existingInclude = this.expression.include;
let finalInclude;
if (existingInclude &&
typeof existingInclude === 'object' &&
typeof include === 'object') {
finalInclude = this.mergeIncludes(existingInclude, include);
}
else {
finalInclude = include;
}
return this.createNew({ include: finalInclude });
}
mergeIncludes(existing, newInclude) {
if (!existing || typeof existing !== 'object')
return newInclude;
if (!newInclude || typeof newInclude !== 'object')
return existing;
const merged = { ...existing };
for (const [key, value] of Object.entries(newInclude)) {
if (key in merged) {
const existingValue = merged[key];
if (typeof existingValue === 'boolean' &&
typeof value === 'object') {
merged[key] = value;
}
else if (typeof value === 'boolean' &&
typeof existingValue === 'object') {
continue;
}
else if (typeof existingValue === 'object' &&
typeof value === 'object' &&
existingValue !== null &&
value !== null) {
merged[key] = this.mergeIncludes(existingValue, value);
}
else {
merged[key] = value;
}
}
else {
merged[key] = value;
}
}
return merged;
}
OrderBy(orderBy) {
return new PrismaOrderedQueryable(this.provider, { ...this.expression, orderBy: [orderBy] }, this.elementType, this.linqContext);
}
SelectMany(selector) {
const resultData = this.ToArrayAsync().then(async (items) => {
const results = [];
for (const item of items) {
const selected = selector(item);
if ('ToArrayAsync' in selected) {
const subResults = await selected.ToArrayAsync();
results.push(...subResults);
}
else {
for await (const subItem of selected) {
results.push(subItem);
}
}
}
return results;
});
return new InMemoryQueryable(resultData);
}
GroupBy(keySelector, elementSelector, resultSelector) {
const resultData = this.ToArrayAsync().then((items) => {
const groups = new Map();
for (const item of items) {
const key = keySelector(item);
const element = elementSelector
? elementSelector(item)
: item;
if (!groups.has(key)) {
groups.set(key, []);
}
groups.get(key).push(element);
}
const results = [];
for (const [key, elements] of groups.entries()) {
if (resultSelector) {
const group = new InMemoryQueryable(elements);
results.push(resultSelector(key, group));
}
else {
results.push({ Key: key, Values: elements });
}
}
return results;
});
return new InMemoryQueryable(resultData);
}
Join(inner, outerKeySelector, innerKeySelector, resultSelector) {
const resultData = Promise.all([
this.ToArrayAsync(),
'ToArrayAsync' in inner
? inner.ToArrayAsync()
: Promise.resolve([]),
]).then(([outerItems, innerItems]) => {
const results = [];
for (const outerItem of outerItems) {
const outerKey = outerKeySelector(outerItem);
for (const innerItem of innerItems) {
const innerKey = innerKeySelector(innerItem);
if (outerKey === innerKey) {
results.push(resultSelector(outerItem, innerItem));
}
}
}
return results;
});
return new InMemoryQueryable(resultData);
}
Union(other) {
const resultData = Promise.all([
this.ToArrayAsync(),
'ToArrayAsync' in other
? other.ToArrayAsync()
: Promise.resolve([]),
]).then(([thisItems, otherItems]) => {
const seen = new Set();
const results = [];
for (const item of [...thisItems, ...otherItems]) {
const key = JSON.stringify(item);
if (!seen.has(key)) {
seen.add(key);
results.push(item);
}
}
return results;
});
return new InMemoryQueryable(resultData);
}
Intersect(other) {
const resultData = Promise.all([
this.ToArrayAsync(),
'ToArrayAsync' in other
? other.ToArrayAsync()
: Promise.resolve([]),
]).then(([thisItems, otherItems]) => {
const otherSet = new Set(otherItems.map((item) => JSON.stringify(item)));
const results = [];
const seen = new Set();
for (const item of thisItems) {
const key = JSON.stringify(item);
if (otherSet.has(key) && !seen.has(key)) {
seen.add(key);
results.push(item);
}
}
return results;
});
return new InMemoryQueryable(resultData);
}
Except(other) {
const resultData = Promise.all([
this.ToArrayAsync(),
'ToArrayAsync' in other
? other.ToArrayAsync()
: Promise.resolve([]),
]).then(([thisItems, otherItems]) => {
const otherSet = new Set(otherItems.map((item) => JSON.stringify(item)));
const results = [];
const seen = new Set();
for (const item of thisItems) {
const key = JSON.stringify(item);
if (!otherSet.has(key) && !seen.has(key)) {
seen.add(key);
results.push(item);
}
}
return results;
});
return new InMemoryQueryable(resultData);
}
Take(count) {
return this.createNew({ take: count });
}
Skip(count) {
return this.createNew({ skip: count });
}
Distinct(keySelector) {
return this.createNew({
distinct: keySelector ? [keySelector] : true,
});
}
Where = this.WhereFilter;
Select = this.SelectFields;
async ExecuteAsync() {
return await this.ToArrayAsync();
}
async ToArrayAsync() {
return await this.provider.Execute(this.expression);
}
async ToListAsync() {
const array = await this.ToArrayAsync();
return new list_1.List(array);
}
async FirstAsync(predicate) {
if (predicate) {
const items = await this.ToArrayAsync();
for (const item of items) {
if (await predicate(item))
return item;
}
throw new Error('No matching element found');
}
else {
const results = await this.createNew({
take: 1,
}).ToArrayAsync();
if (results.length === 0) {
throw new Error('Sequence contains no elements');
}
return results[0];
}
}
async FirstOrDefaultAsync(predicate) {
try {
return await this.FirstAsync(predicate);
}
catch {
return undefined;
}
}
async LastAsync(predicate) {
const items = await this.ToArrayAsync();
if (predicate) {
for (let i = items.length - 1; i >= 0; i--) {
if (await predicate(items[i]))
return items[i];
}
throw new Error('No matching element found');
}
if (items.length === 0) {
throw new Error('Sequence contains no elements');
}
return items[items.length - 1];
}
async LastOrDefaultAsync(predicate) {
try {
return await this.LastAsync(predicate);
}
catch {
return undefined;
}
}
async SingleAsync(predicate) {
const items = await this.ToArrayAsync();
let found;
let count = 0;
for (const item of items) {
if (!predicate || (await predicate(item))) {
found = item;
count++;
if (count > 1) {
throw new Error('Sequence contains more than one element');
}
}
}
if (count === 0) {
throw new Error('Sequence contains no elements');
}
return found;
}
async SingleOrDefaultAsync(predicate) {
try {
return await this.SingleAsync(predicate);
}
catch (error) {
if (error.message.includes('more than one element')) {
throw error;
}
return undefined;
}
}
async CountAsync(predicate) {
if (predicate) {
const items = await this.ToArrayAsync();
let count = 0;
for (const item of items) {
if (await predicate(item))
count++;
}
return count;
}
else {
const items = await this.ToArrayAsync();
return items.length;
}
}
async AnyAsync(predicate) {
if (predicate) {
const items = await this.ToArrayAsync();
for (const item of items) {
if (await predicate(item))
return true;
}
return false;
}
else {
const results = await this.createNew({
take: 1,
}).ToArrayAsync();
return results.length > 0;
}
}
async AllAsync(predicate) {
const items = await this.ToArrayAsync();
for (const item of items) {
if (!(await predicate(item)))
return false;
}
return true;
}
async MinAsync(selector) {
const items = await this.ToArrayAsync();
if (items.length === 0) {
throw new Error('Sequence contains no elements');
}
if (selector) {
return items
.map(selector)
.reduce((min, current) => (current < min ? current : min));
}
return items.reduce((min, current) => (current < min ? current : min));
}
async MaxAsync(selector) {
const items = await this.ToArrayAsync();
if (items.length === 0) {
throw new Error('Sequence contains no elements');
}
if (selector) {
return items
.map(selector)
.reduce((max, current) => (current > max ? current : max));
}
return items.reduce((max, current) => (current > max ? current : max));
}
async SumAsync(selector) {
const items = await this.ToArrayAsync();
return items
.map(selector || ((x) => x))
.reduce((sum, current) => sum + current, 0);
}
async AverageAsync(selector) {
const items = await this.ToArrayAsync();
const values = items.map(selector || ((x) => x));
return (values.reduce((sum, current) => sum + current, 0) / values.length);
}
async LongCountAsync(predicate) {
return await this.CountAsync(predicate);
}
async AggregateAsync(seed, func) {
const items = await this.ToArrayAsync();
let accumulator = seed;
for (const item of items) {
accumulator = await func(accumulator, item);
}
return accumulator;
}
async ContainsAsync(item) {
const items = await this.ToArrayAsync();
return items.includes(item);
}
async ForEachAsync(action) {
const items = await this.ToArrayAsync();
for (const item of items) {
await action(item);
}
}
async ToEnumerable() {
const array = await this.ToArrayAsync();
return new enumerable_1.Enumerable(array);
}
async ToAsyncEnumerable() {
const array = await this.ToArrayAsync();
return async_enumerable_1.AsyncEnumerable.fromArray(array);
}
async AsAsyncEnumerable() {
return await this.ToAsyncEnumerable();
}
async AsEnumerable() {
return await this.ToEnumerable();
}
async ToEnumerableAsync() {
return await this.ToEnumerable();
}
}
exports.PrismaQueryable = PrismaQueryable;
class PrismaOrderedQueryable extends PrismaQueryable {
Take(count) {
return new PrismaOrderedQueryable(this.provider, { ...this.expression, take: count }, this.elementType, this.linqContext);
}
Skip(count) {
return new PrismaOrderedQueryable(this.provider, { ...this.expression, skip: count }, this.elementType, this.linqContext);
}
Distinct(keySelector) {
return new PrismaOrderedQueryable(this.provider, {
...this.expression,
distinct: keySelector ? [keySelector] : true,
}, this.elementType, this.linqContext);
}
ThenBy(orderBy) {
const currentOrderBy = this.expression.orderBy || [];
return new PrismaOrderedQueryable(this.provider, { ...this.expression, orderBy: [...currentOrderBy, orderBy] }, this.elementType, this.linqContext);
}
ThenByDescending(orderBy) {
const currentOrderBy = this.expression.orderBy || [];
return new PrismaOrderedQueryable(this.provider, { ...this.expression, orderBy: [...currentOrderBy, orderBy] }, this.elementType, this.linqContext);
}
}
exports.PrismaOrderedQueryable = PrismaOrderedQueryable;
class InMemoryQueryable {
data;
constructor(data) {
this.data = data;
}
get Provider() {
throw new Error('InMemoryQueryable does not support Provider');
}
get Expression() {
return null;
}
get ElementType() {
return null;
}
WhereFilter(where) {
throw new Error('WhereFilter not supported in InMemoryQueryable');
}
SelectFields(select) {
throw new Error('SelectFields not supported in InMemoryQueryable');
}
Include(include) {
throw new Error('Include not supported in InMemoryQueryable');
}
OrderBy(orderBy) {
throw new Error('OrderBy not supported in InMemoryQueryable');
}
SelectMany(selector) {
const newData = this.resolveData().then(async (items) => {
const results = [];
for (const item of items) {
const result = selector(item);
if (result &&
typeof result.ToArrayAsync === 'function') {
const arr = await result.ToArrayAsync();
results.push(...arr);
}
else if (result &&
typeof result.ToArrayAsync === 'function') {
const arr = await result.ToArrayAsync();
results.push(...arr);
}
}
return results;
});
return new InMemoryQueryable(newData);
}
GroupBy(keySelector, elementSelector, resultSelector) {
const newData = this.resolveData().then((items) => {
const groups = new Map();
for (const item of items) {
const key = keySelector(item);
const value = elementSelector
? elementSelector(item)
: item;
if (!groups.has(key))
groups.set(key, []);
groups.get(key).push(value);
}
const result = [];
for (const [key, group] of groups.entries()) {
if (resultSelector) {
const groupQueryable = new InMemoryQueryable(group);
result.push(resultSelector(key, groupQueryable));
}
else {
result.push({ key, group });
}
}
return result;
});
return new InMemoryQueryable(newData);
}
Join(inner, outerKeySelector, innerKeySelector, resultSelector) {
const newData = this.resolveData().then(async (outerItems) => {
const innerItems = await inner.ToArrayAsync();
const results = [];
for (const o of outerItems) {
const oKey = outerKeySelector(o);
for (const i of innerItems) {
if (oKey === innerKeySelector(i)) {
results.push(resultSelector(o, i));
}
}
}
return results;
});
return new InMemoryQueryable(newData);
}
Take(count) {
const newData = this.resolveData().then((items) => items.slice(0, count));
return new InMemoryQueryable(newData);
}
Skip(count) {
const newData = this.resolveData().then((items) => items.slice(count));
return new InMemoryQueryable(newData);
}
Distinct(keySelector) {
const newData = this.resolveData().then((items) => {
if (keySelector) {
const seen = new Set();
return items.filter((item) => {
const key = keySelector(item);
if (seen.has(key))
return false;
seen.add(key);
return true;
});
}
else {
return Array.from(new Set(items));
}
});
return new InMemoryQueryable(newData);
}
Union(other) {
const newData = this.resolveData().then(async (arr1) => {
const arr2 = await other.ToArrayAsync();
return Array.from(new Set([...arr1, ...arr2]));
});
return new InMemoryQueryable(newData);
}
Intersect(other) {
const newData = this.resolveData().then(async (arr1) => {
const arr2 = await other.ToArrayAsync();
return arr1.filter((x) => arr2.includes(x));
});
return new InMemoryQueryable(newData);
}
Except(other) {
const newData = this.resolveData().then(async (arr1) => {
const arr2 = await other.ToArrayAsync();
return arr1.filter((x) => !arr2.includes(x));
});
return new InMemoryQueryable(newData);
}
Where = this.WhereFilter;
Select = this.SelectFields;
async ToArrayAsync() {
return await this.resolveData();
}
async ToListAsync() {
const array = await this.ToArrayAsync();
return new list_1.List(array);
}
async FirstAsync(predicate) {
const items = await this.resolveData();
if (predicate) {
for (const item of items) {
if (await predicate(item))
return item;
}
throw new Error('No matching element found');
}
if (items.length === 0) {
throw new Error('Sequence contains no elements');
}
return items[0];
}
async FirstOrDefaultAsync(predicate) {
try {
return await this.FirstAsync(predicate);
}
catch {
return undefined;
}
}
async LastAsync(predicate) {
const items = await this.resolveData();
if (predicate) {
for (let i = items.length - 1; i >= 0; i--) {
if (await predicate(items[i]))
return items[i];
}
throw new Error('No matching element found');
}
if (items.length === 0) {
throw new Error('Sequence contains no elements');
}
return items[items.length - 1];
}
async LastOrDefaultAsync(predicate) {
try {
return await this.LastAsync(predicate);
}
catch {
return undefined;
}
}
async SingleAsync(predicate) {
const items = await this.resolveData();
let found;
let count = 0;
for (const item of items) {
if (!predicate || (await predicate(item))) {
found = item;
count++;
if (count > 1) {
throw new Error('Sequence contains more than one element');
}
}
}
if (count === 0) {
throw new Error('Sequence contains no elements');
}
return found;
}
async SingleOrDefaultAsync(predicate) {
try {
return await this.SingleAsync(predicate);
}
catch (error) {
if (error.message.includes('more than one element')) {
throw error;
}
return undefined;
}
}
async CountAsync(predicate) {
const items = await this.resolveData();
if (predicate) {
let count = 0;
for (const item of items) {
if (await predicate(item))
count++;
}
return count;
}
return items.length;
}
async AnyAsync(predicate) {
const items = await this.resolveData();
if (predicate) {
for (const item of items) {
if (await predicate(item))
return true;
}
return false;
}
return items.length > 0;
}
async AllAsync(predicate) {
const items = await this.resolveData();
for (const item of items) {
if (!(await predicate(item)))
return false;
}
return true;
}
async MinAsync(selector) {
const items = await this.resolveData();
if (items.length === 0) {
throw new Error('Sequence contains no elements');
}
if (selector) {
return items
.map(selector)
.reduce((min, current) => (current < min ? current : min));
}
return items.reduce((min, current) => (current < min ? current : min));
}
async MaxAsync(selector) {
const items = await this.resolveData();
if (items.length === 0) {
throw new Error('Sequence contains no elements');
}
if (selector) {
return items
.map(selector)
.reduce((max, current) => (current > max ? current : max));
}
return items.reduce((max, current) => (current > max ? current : max));
}
async SumAsync(selector) {
const items = await this.resolveData();
return items
.map(selector || ((x) => x))
.reduce((sum, current) => sum + current, 0);
}
async AverageAsync(selector) {
const items = await this.resolveData();
const values = items.map(selector || ((x) => x));
return (values.reduce((sum, current) => sum + current, 0) / values.length);
}
async AggregateAsync(seed, func) {
const items = await this.resolveData();
let accumulator = seed;
for (const item of items) {
accumulator = await func(accumulator, item);
}
return accumulator;
}
async ContainsAsync(item) {
const items = await this.resolveData();
return items.includes(item);
}
async ToEnumerable() {
const array = await this.ToArrayAsync();
return new enumerable_1.Enumerable(array);
}
async ToAsyncEnumerable() {
const array = await this.ToArrayAsync();
return async_enumerable_1.AsyncEnumerable.fromArray(array);
}
async AsAsyncEnumerable() {
return await this.ToAsyncEnumerable();
}
async AsEnumerable() {
return await this.ToEnumerable();
}
async ToEnumerableAsync() {
return await this.ToEnumerable();
}
async ExecuteAsync() {
return await this.ToArrayAsync();
}
async LongCountAsync(predicate) {
return await this.CountAsync(predicate);
}
async ForEachAsync(action) {
const items = await this.resolveData();
for (const item of items) {
await action(item);
}
}
async resolveData() {
return Array.isArray(this.data) ? this.data : await this.data;
}
}
exports.InMemoryQueryable = InMemoryQueryable;
//# sourceMappingURL=prisma-queryable.js.map