@dataql/node
Version:
DataQL core SDK for unified data management with MongoDB and GraphQL - Production Multi-Cloud Ready
265 lines (264 loc) • 10.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DocumentScope = exports.SubdocumentCollection = void 0;
exports.createDocumentScope = createDocumentScope;
const utils_js_1 = require("./utils.js");
class SubdocumentCollection {
constructor(collectionName, parentFilter, subdocumentPath, schema, fetchFunction, appToken, ensureSession) {
this.collectionName = collectionName;
this.parentFilter = parentFilter;
this.subdocumentPath = subdocumentPath;
this.schema = schema;
this.fetchFunction = fetchFunction;
this.appToken = appToken;
this.ensureSession = ensureSession;
}
getWorkerUrl() {
return "https://edge.dataql.com";
}
async create(data) {
const sessionId = await this.ensureSession();
const url = `${this.getWorkerUrl()}/data/${this.collectionName}/subdocument/${this.subdocumentPath}/create`;
const res = await this.fetchFunction(url, {
method: "POST",
headers: {
"content-type": "application/json",
"x-app-token": this.appToken,
},
body: JSON.stringify({
sessionId,
schema: { [this.collectionName]: this.schema },
parentFilter: this.parentFilter,
data,
}),
});
const result = await res.json();
if (result.error)
throw new Error(result.error);
return result;
}
async createUnique(data) {
// First, get the subdocument schema for this path
const pathParts = this.subdocumentPath.split(".");
let currentSchema = this.schema;
for (const part of pathParts) {
if (currentSchema[part]) {
currentSchema = Array.isArray(currentSchema[part])
? currentSchema[part][0]
: currentSchema[part];
}
}
// Extract comparable fields from the new data
const comparableFields = (0, utils_js_1.extractComparableFields)(data, currentSchema);
// Find existing subdocuments
const existingDocs = await this.find();
// Check if any existing subdocument matches the comparable fields
for (const existingDoc of existingDocs) {
const existingComparableFields = (0, utils_js_1.extractComparableFields)(existingDoc, currentSchema);
if ((0, utils_js_1.areFieldsMatching)(comparableFields, existingComparableFields)) {
// Found a matching subdocument, return it
return {
result: existingDoc,
isExisting: true,
};
}
}
// No matching subdocument found, create a new one
const createResult = await this.create(data);
return {
result: createResult,
isExisting: false,
};
}
async find(filter = {}) {
const sessionId = await this.ensureSession();
const url = `${this.getWorkerUrl()}/data/${this.collectionName}/subdocument/${this.subdocumentPath}/find`;
const res = await this.fetchFunction(url, {
method: "POST",
headers: {
"content-type": "application/json",
"x-app-token": this.appToken,
},
body: JSON.stringify({
sessionId,
schema: { [this.collectionName]: this.schema },
parentFilter: this.parentFilter,
filter,
}),
});
const result = await res.json();
if (result.error)
throw new Error(result.error);
return Array.isArray(result) ? result : [result];
}
async update(filter, update) {
const sessionId = await this.ensureSession();
const url = `${this.getWorkerUrl()}/data/${this.collectionName}/subdocument/${this.subdocumentPath}/update`;
const res = await this.fetchFunction(url, {
method: "POST",
headers: {
"content-type": "application/json",
"x-app-token": this.appToken,
},
body: JSON.stringify({
sessionId,
schema: { [this.collectionName]: this.schema },
parentFilter: this.parentFilter,
filter,
update,
}),
});
const result = await res.json();
if (result.error)
throw new Error(result.error);
return result;
}
async delete(filter) {
const sessionId = await this.ensureSession();
const url = `${this.getWorkerUrl()}/data/${this.collectionName}/subdocument/${this.subdocumentPath}/delete`;
const res = await this.fetchFunction(url, {
method: "POST",
headers: {
"content-type": "application/json",
"x-app-token": this.appToken,
},
body: JSON.stringify({
sessionId,
schema: { [this.collectionName]: this.schema },
parentFilter: this.parentFilter,
filter,
}),
});
const result = await res.json();
if (result.error)
throw new Error(result.error);
return result;
}
async updateMany(filter, update) {
const sessionId = await this.ensureSession();
const url = `${this.getWorkerUrl()}/data/${this.collectionName}/subdocument/${this.subdocumentPath}/updateMany`;
const res = await this.fetchFunction(url, {
method: "POST",
headers: {
"content-type": "application/json",
"x-app-token": this.appToken,
},
body: JSON.stringify({
sessionId,
schema: { [this.collectionName]: this.schema },
parentFilter: this.parentFilter,
filter,
update,
}),
});
const result = await res.json();
if (result.error)
throw new Error(result.error);
return result;
}
async deleteMany(filter) {
const sessionId = await this.ensureSession();
const url = `${this.getWorkerUrl()}/data/${this.collectionName}/subdocument/${this.subdocumentPath}/deleteMany`;
const res = await this.fetchFunction(url, {
method: "POST",
headers: {
"content-type": "application/json",
"x-app-token": this.appToken,
},
body: JSON.stringify({
sessionId,
schema: { [this.collectionName]: this.schema },
parentFilter: this.parentFilter,
filter,
}),
});
const result = await res.json();
if (result.error)
throw new Error(result.error);
return result;
}
}
exports.SubdocumentCollection = SubdocumentCollection;
class DocumentScope {
constructor(collectionName, parentFilter, schema, fetchFunction, appToken, ensureSession) {
this.collectionName = collectionName;
this.parentFilter = parentFilter;
this.schema = schema;
this.fetchFunction = fetchFunction;
this.appToken = appToken;
this.ensureSession = ensureSession;
this.subdocuments = {};
this.initializeSubdocuments();
}
initializeSubdocuments() {
this.analyzeSchemaForSubdocuments(this.schema, "");
}
analyzeSchemaForSubdocuments(schemaObj, path) {
for (const [key, field] of Object.entries(schemaObj)) {
const currentPath = path ? `${path}.${key}` : key;
if (this.isSubdocumentArray(field)) {
// Create subdocument collection for arrays
this.subdocuments[key] = new SubdocumentCollection(this.collectionName, this.parentFilter, currentPath, this.schema, this.fetchFunction, this.appToken, this.ensureSession);
}
else if (this.isNestedObject(field)) {
// For nested objects, create a proxy that allows further nesting
this.createNestedProxy(key, field, currentPath);
}
}
}
isSubdocumentArray(field) {
return (Array.isArray(field) && field.length > 0 && typeof field[0] === "object");
}
isNestedObject(field) {
return (typeof field === "object" &&
field !== null &&
!Array.isArray(field) &&
!field.type &&
!field.enum);
}
createNestedProxy(key, field, path) {
const nestedScope = {
update: async (updateData) => {
const sessionId = await this.ensureSession();
const url = `${this.getWorkerUrl()}/data/${this.collectionName}/subdocument/${path}/update`;
const res = await this.fetchFunction(url, {
method: "POST",
headers: {
"content-type": "application/json",
"x-app-token": this.appToken,
},
body: JSON.stringify({
sessionId,
schema: { [this.collectionName]: this.schema },
parentFilter: this.parentFilter,
update: updateData,
}),
});
const result = await res.json();
if (result.error)
throw new Error(result.error);
return result;
},
};
// Add nested subdocuments
this.analyzeSchemaForSubdocuments(field, path);
// Assign the nested scope to this object
this[key] = nestedScope;
}
getWorkerUrl() {
return "https://edge.dataql.com";
}
}
exports.DocumentScope = DocumentScope;
// Create a Proxy to enable dynamic property access
function createDocumentScope(collectionName, parentFilter, schema, fetchFunction, appToken, ensureSession) {
const scope = new DocumentScope(collectionName, parentFilter, schema, fetchFunction, appToken, ensureSession);
return new Proxy(scope, {
get(target, prop) {
if (typeof prop === "string" && prop in target.subdocuments) {
return target.subdocuments[prop];
}
return target[prop];
},
});
}