JUSTAOS’s ODM

JUSTAOS’s ODM (Object Document Mapper) is built for Deno and provides transparent persistence for JavaScript objects to MongoDB database.

Supports schemas with multi-level inheritance. Also supports interception on operations (create, read, update and delete).

GitHub release (with filter) Build Coverage License

import {ODM} from 'https://deno.land/x/justaos_odm@4.4.3/mod.ts';

Database connection

const odm = new ODM();

try {
  await odm.connect("mongodb://127.0.0.1:27017/collection-service");
  console.log("connection success");
  const isDbExists = await odm.databaseExists();

  if (isDbExists) {
    console.log("db exists");
  } else {
    console.log("db does not exists");
  }
} catch (_error) {
  console.log("connection failed");
} finally {
  await odm.closeConnection();
}

Querying

odm.defineCollection({
  name: "teacher",
  fields: [
    {
      name: "name",
      type: "string"
    },
    {
      name: "roll_no",
      type: "integer"
    }
  ]
});

let teacherCollection = odm.collection("teacher");
for (let i = 0; i < 10; i++) {
  let teacherRecord = teacherCollection.createNewRecord();
  teacherRecord.set("name", "a" + (i + 1));
  teacherRecord.set("roll_no", i + 1);
  await teacherRecord.insert();
}

teacherCollection
  .find({}, { sort: { roll_no: -1 } })
  .toArray()
  .then(function (records) {
    records.forEach(async function (rec) {
      console.log(
        (await rec.getDisplayValue("name")) +
          " :: " +
          (await rec.getDisplayValue("roll_no"))
      );
      console.log(JSON.stringify(await rec.toObject(), null, 4));
    });
  });

teacherCollection.count().then(function (count) {
  console.log(count);
  odm.closeConnection();
});

Intercepting database operations

import getODM from "./getODM.ts";
import {
  OperationInterceptorInterface,
  OperationType,
  OperationWhen,
  Record
} from "../mod.ts";

const odm = await getODM();

odm.addInterceptor(
  new (class extends OperationInterceptorInterface {
    getName() {
      return "my-intercept";
    }

    async intercept(
      collectionName: string,
      operation: OperationType,
      when: OperationWhen,
      records: Record[],
      context: any
    ) {
      if (collectionName === "student") {
        if (operation === "CREATE") {
          console.log(
            "[collectionName=" +
              collectionName +
              ", operation=" +
              operation +
              ", when=" +
              when +
              "]"
          );
          if (when === "BEFORE") {
            for (let record of records) {
              console.log(
                "computed field updated for :: " + record.get("name")
              );
              record.set("computed", record.get("name") + " +++ computed");
            }
          }
        }
        if (operation === "READ") {
          console.log(
            "[collectionName=" +
              collectionName +
              ", operation=" +
              operation +
              ", when=" +
              when +
              "]"
          );
          if (when === "AFTER") {
            for (const record of records)
              console.log(JSON.stringify(record.toObject(), null, 4));
          }
        }
      }
      return records;
    }
  })()
);

odm.defineCollection({
  name: "student",
  fields: [
    {
      name: "name",
      type: "string"
    },
    {
      name: "computed",
      type: "string"
    }
  ]
});

const studentCollection = odm.collection("student");
const studentRecord = studentCollection.createNewRecord();
studentRecord.set("name", "John " + new Date().toISOString());
studentRecord.insert().then(function () {
  studentCollection
    .find()
    .toArray()
    .then(function () {
      odm.closeConnection();
    });
});

Define custom field type

After connection established, you can define custom field type.

import getODM from "./getODM.ts";
import { FieldType, PrimitiveDataType, Schema, ODM } from "../mod.ts";

const odm = await getODM();

odm.addFieldType(
  class extends FieldType {
    constructor(odm: ODM) {
      super(odm, PrimitiveDataType.STRING);
    }

    getName() {
      return "email";
    }

    async validateValue(schema: Schema, fieldName: string, record: any, context: any) {
      const pattern = "(.+)@(.+){2,}\.(.+){2,}";
      if (!new RegExp(pattern).test(record[fieldName]))
        throw new Error("Not a valid email");
    }

    validateDefinition(fieldDefinition: any) {
      return !!fieldDefinition.name;
    }

    async getDisplayValue(schema: Schema, fieldName: string, record: any) {
      return record[fieldName];
    }

    setValueIntercept(schema: Schema, fieldName: string, newValue: any): any {
      return newValue;
    }
  }
);

odm.defineCollection({
  label: "Student",
  name: "student",
  fields: [
    {
      name: "name",
      type: "string"
    },
    {
      name: "personal_contact",
      type: "email"
    },
    {
      name: "emp_no",
      type: "objectId"
    },
    {
      name: "salary",
      type: "integer"
    },
    {
      name: "birth_date",
      type: "date"
    },
    {
      name: "gender",
      type: "boolean"
    },
    {
      name: "address",
      type: "object"
    }
  ]
});

const studentCollection = odm.collection("student");
const studentRecord = studentCollection.createNewRecord();
studentRecord.set("personal_contact", "test");
studentRecord.set("birth_date", new Date());
studentRecord.insert().then(
  function () {
    console.log("Student created");
  },
  (err) => {
    console.log(err.toJSON());
    odm.closeConnection().then(function () {
      console.log("Connection closed");
    });
  }
);

Inheritance

import getODM from "./getODM.ts";

const odm = await getODM();

odm.defineCollection({
  name: "animal",
  fields: [
    {
      name: "name",
      type: "string"
    }
  ]
});

const animalCol = odm.collection("animal");
const animal = animalCol.createNewRecord();
animal.set("name", "Puppy");
await animal.insert();

odm.defineCollection({
  name: "dog",
  extends: "animal",
  final: true,
  fields: [
    {
      name: "breed",
      type: "string"
    }
  ]
});

const dogCol = odm.collection("dog");
const husky = dogCol.createNewRecord();
husky.set("name", "Jimmy");
husky.set("breed", "Husky");
await husky.insert();

animalCol
  .find({})
  .toArray()
  .then(function (animals) {
    animals.forEach((animal) => {
      console.log(animal.toObject());
    });
    odm.closeConnection();
  });

Check the examples >> here <<

Code of Conduct

Contributor Covenant