npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ts-mongodb-orm

v2.0.1

Published

ORM for MongoDB with 0 dependencies. Ready for production use.

Downloads

188

Readme

ts-mongodb-orm (Typescript Orm wrapper for Mongodb)

ORM for MongoDB with 0 dependencies. Ready for production use.

NPM version Test coverage

The codes are well written in consistency and intuitive with respect to the original mongodb library.

Please check the examples below to check out all the amazing features!

Prerequisite

This library have zero dependencies, which means it works with your own mongodb package version. To start with, simply install the official mongodb package and its type.

npm install --save mongodb @types/mongodb

To use with [email protected] versions, please use Nodejs 12 or above.

This library has been tested with the follow mongodb versions:

Project Setup

  • npm install -s mongodb@latest
  • npm install -s ts-mongodb-orm@latest
  • In tsconfig.json
    • set "experimentalDecorators" to true.
    • set "emitDecoratorMetadata" to true.
    • set "strictNullChecks" to true.

Example: Quick Start


import { Binary, createConnection, Document, Field, Index, ObjectId } from "ts-mongodb-orm";

@Index({numberValue: -1})
@Document({collectionName: "CustomCollectionName"}) // default to Class name
class QuickStart {
    @Field()
    public _id!: ObjectId;

    @Field()
    public stringValue?: string;

    @Field()
    public numberValue: number = 0;

    @Field()
    public booleanValue: boolean = false;

    @Field({index: -1})
    public dateValue: Date = new Date();

    @Field()
    public arrayValue: number[] = [1, 2, 3];

    @Field()
    public objectArrayValue: any = {};

    @Field()
    public binary: Binary = new Binary(Buffer.alloc(0));
}

async function quickStartExample() {
    const connection = await createConnection({
        uri: "mongodb+srv://USERNAME:[email protected]",
        dbName: "DbName",
        mongoClientOptions: {
            w: "majority",
            ignoreUndefined: true, // preventing saving null value in server side
            serverSelectionTimeoutMS: 5000,
        },
    });

    // operations
    const repository = connection.getRepository(QuickStart);
    await repository.createCollection({strict: true}); // throw error if collection already exist
    await repository.syncIndex();
    await repository.dropCollection();

    // documents operations
    const document1 = new QuickStart();
    document1.stringValue = "hello world 1";
    document1.numberValue = 999;

    await repository.insert(document1);
    await repository.update(document1);
    await repository.delete(document1);

    // query
    const findDocument1 = await repository.findOne(document1._id);
    const findDocument2 = await repository.query().filter("_id", document1._id).findOne();
    const aggregate1 = await repository.aggregate().count("numberValue").findOne();

    // transaction
    const transactionManager = connection.getTransactionManager();
    await transactionManager.startTransaction(async (session) => {
        const document2 = repository.create({stringValue: "hello world 2"});
        await repository.insert(document2, {session});
    });

    // atomic lock
    const lockManager = connection.getLockManager();
    await lockManager.createCollection(); // this need to be called once to create the collection
    await lockManager.startLock("lockKey", async () => {
        //
    });
}

Example: Query

async function queryExample() {
    // findOne all documents
    const allDocuments = await repository.query().findMany();

    // async iterator
    const iterator = await repository.query().filter("numberValue", 1).getAsyncIterator();
    for await (const document1 of iterator) {
        // handle document
    }

    // delete many by condition
    const query2 = repository.query();
    const deletedTotal = await query2
        .filter("numberValue", 1)
        .getDeleter()
        .deleteMany();

    // update many by condition
    const query3 = repository.query();
    const updatedTotal = await query3
        .filter("numberValue", 1)
        .getUpdater()
        .inc("numberValue", 10)
        .updateMany();

    // all kind of complex query
    const query4 = repository.query({weakType: true})
        .filter("field1", x => x.elemMatchObject(y => y.filter("hello", 1)))
        .filter("field2", x => x.elemMatch(y => y.gt(5)))
        .filter("field3", x => x.gt(5).lt(3).lte(4).gte(6))
        .filter("field4", x => x.in([1, 2, 3]))
        .filter("field5", x => x.nin([1, 2, 3]))
        .filter("field6", x => x.size(3))
        .filter("field7", x => x.mod(10, 1))
        .filter("field8", x => x.not(y => y.gt(5)))
        .filter("field9", x => x.regex("/abcd/"))
        .text("hello-world")
        .or(x => {
            x.filter("fieldA.a", 5)
                .or("fieldA.b", y => y.exists(false))
                .or("fieldA.c", y => y.type(mongodbDataTypes.array));
        })
        .and(x => {
            x.filter("fieldB.a", y => y.bitsAllClear(1))
                .filter("fieldB.b", y => y.bitsAllSet([1, 2]))
                .filter("fieldB.c", y => y.bitsAnyClear(1))
                .filter("fieldB.d", y => y.bitsAnySet([1, 2]));
        })
        .nor(x => {
            x.filter("fieldC.a", 7);
        });
}

Example: Aggregate

async function aggregateExample() {
    // iterator
    const iterator = await repository.aggregate({allowDiskUse: true, maxTimeMS: 5000})
        .match(x => x.filter("numberValue", 1))
        .project({_id: 1})
        .getAsyncIterator();

    for await (const item of iterator) {
    }
    
    // find total
    const result1 = await repository.aggregate()
        .match(x => x.filter("numberValue", 1))
        .count("total")
        .findOne();

    // skip
    const results2 = await repository.aggregate()
        .sort({index: 1})
        .skip(5)
        .limit(10)
        .findMany();

    // some field you may want weakType
    const result3 = await repository.aggregate({weakType: true})
        .match(x => x.filter("anyFieldName", 1))
        .findOne();

    // cast to document directly
        const result4 = await repository.aggregate()
            .toDocument()
            .findOne();
        const {stringValue} = result4!;
}

Example: Transaction

async function transactionExample() {
    const transactionManager1 = connection.getTransactionManager({maxRetry: 2});

    try {
        const result = await transactionManager1.startTransaction(async (session) => {
            const document1 = new QuickStart();
            await repository.insert(document1, {session});

            const foundDocument1 = await repository.findOne(document1._id, {session});
            const foundDocument2 = await repository.query({session})
                .filter("_id", document1._id)
                .findOne();

            // we can abort transaction
            if (1 < 2) {
                await session.abortTransaction();
            }

            return [1, 2, 3];
        });

        // value = [1, 2, 3];
        const {value, hasCommitted} = result;
    } catch (err) {
        // manage error
    }
}

Example: Manage Index

async function manageIndexExample() {
    @Index({name: 1, value: -1}, {sparse: true})
    @Index({uniqueField: 1}, {unique: true})
    @Index({dateField: 1}, {expireAfterSeconds: 10})
    @Index({filterField: 1}, {partialFilterExpression: {numberValue: {$gt: 5}}})
    @Index({textField: "text"})
    @Document()
    class IndexDocument {
        @Field()
        public _id!: ObjectId;

        @Field()
        public uniqueField?: string;

        @Field()
        public dateField?: Date;

        @Field()
        public filterField?: string;

        @Field()
        public textField?: string;

        @Field()
        public numberValue?: number;
    }

    const repository1 = connection.getRepository(IndexDocument);

    // syncIndexes will drop non exist index and then new a new one
    // this will also create collection if not exist
    await repository1.syncIndex();

    // addIndexes will only try to new new one
    // this will also create collection if not exist
    await repository1.addIndex();

    // drop all index
    await repository1.dropIndex();

    // compare the existing index with decorators
    await repository1.compareIndex();
}

Example: Watch

async function watchExample() {
    const stream = repository.watch();
    stream.on("error", err => {
        // any possible error
    });

    stream.on("insert", next => {
        const {document, documentKey, operationType} = next;
    });

    stream.on("update", next => {
    });

    stream.on("delete", next => {
    });

    stream.on("change", next => {
        // any type of operations includes insert, update, delete, replace, drop, dropDatabase, rename, invalidate
    });

    stream.on("end", () => {
        // steam is ended
    });

    stream.on("close", () => {
        // steam is closed
    });
}

Example: Hook

async function hookExample() {
    @Document()
    class HookDocument {
        @Field()
        public _id!: ObjectId;

        @AfterLoad()
        public afterLoad() {
            // this won't await for promise
        }

        @BeforeUpsert()
        @BeforeInsert()
        @BeforeUpdate()
        @BeforeDelete()
        public before(type: string) { // type: afterLoad, upsert, insert, update, delete
            // this won't await for promise
        }
    }

    const repository1 = connection.getRepository(HookDocument);
    const document1 = repository1.create();
    await repository1.insert(document1);
}

Example: Schema Validation

async function schemaValidationExample() {
    @Document()
    class SchemaValidationDocument {
        @Field()
        public _id!: ObjectId;

        @Field({isRequired: true, schema: {bsonType: "string"}})
        public stringField?: string;

        @Field({isRequired: true, schema: {bsonType: "date"}})
        public dateField?: Date;

        @Field({isRequired: true, schema: {type: "number", minimum: 10, exclusiveMinimum: true}})
        public numberField?: number;

        @Field({schema: {bsonType: "object", additionalProperties: true, properties: {name: {bsonType: "string"}}}})
        public objectField?: any;
    }

    const repository1 = connection.getRepository(SchemaValidationDocument);

    // this will also create collection with validation
    await repository1.createCollection();

    // or you can sync the validation later on (this need admin right)
    await repository1.syncSchemaValidation();

    // view existing validation
    const options = await repository1.getCollection().options();

    try {
        const document1 = repository1.create();
        await repository1.insert(document1);
    } catch (err) {
        // err.message === "Document failed validation"
    }
}

Example: Error

async function errorExample() {
    // allow you to debug async error more easily (default: true)
    tsMongodbOrm.useFriendlyErrorStack = true;

    const document1 = new QuickStart();
    await repository.insert(document1);
    await repository.delete(document1);

    try {
        await repository.update(document1);
    } catch (err) {
        if (err instanceof TsMongodbOrmError) {
            // error managed by this library

        } else if (err instanceof MongoError) {
            // error from the native mongodb library

        } else {
            // other error

        }
    }
}

Example: Buffer

async function bufferExample() {
    // for some reason, promoteBuffers will cause some initialize error in [email protected]
    const connection = await createConnection({
        uri: "mongodb+srv://USERNAME:[email protected]",
        dbName: "DbName",
        mongoClientOptions: {
            w: "majority",
            useNewUrlParser: true,
            useUnifiedTopology: true,
            ignoreUndefined: true, // preventing saving null value in server side
            promoteBuffers: true, // if you wanted to use native JS buffer directly
        },
    });

    @Document()
    class BufferDocument {
        @Field()
        public _id!: ObjectId;

        @Field()
        public buffer: Buffer = Buffer.alloc(0);

        @Field()
        public bufferObject?: {buffer: Buffer};
    }

    const repository = connection.getRepository(BufferDocument);
    const document = await repository.create({buffer: Buffer.alloc(10)});
    await repository.insert(document);

    const findDocument = await repository.findOne(document._id);
    if (findDocument && findDocument.buffer instanceof Buffer) {
        // true
    }
}

More Detailed Examples

Examples are in the tests/ directory.

| Sample | Source Code | | --------------------------- | --------------------------------- | | General | source code | | Active Record | source code | | Aggregate | source code | | Capped | source code | | Compatibility | source code | | Error | source code | | Index | source code | | LockManager | source code | | RankManager | source code | | Transaction Manager | source code | | Watch | source code | | Query | source code | | Hook | source code | | Schema Validation | source code |

Useful links

  • https://www.npmjs.com/package/mongodb
  • https://docs.mongodb.com/manual/
  • https://docs.mongodb.com/manual/core/schema-validation/