Lucid Mongo
lucid-mongo is a mongo query builder and ORM. It also has support for database migrations, seeds and factories as @adonis/lucid.
:pray: This repository is base on @adonis/lucid and only work with mongodb.
Features?
Apart from being just a query builder, lucid-mongo has following features.
- ES6 classes based data Models.
- Model Hooks
- Associations
- Serializers ( Vanilla and JSON API )
- Migrations
- Factories and Seeds
Lucid-mongo version 2.0 now can be used as standalone or used with AdonisJS
You can learn more about AdonisJS and all of its awesomeness on http://adonisjs.com :evergreen_tree:
You can see example with AdonisJS framework here adonis-mongodb-boilerplate
Node/OS Target
This repo/branch is supposed to run fine on all major OS platforms and targets Node.js >=8.0
Installation
$ npm i @izorwebid/lucid-mongo-2
Breaking update
Adonis version | Lucid Mongo version |
---|
4.x.x | 3.x.x |
const users = await User.where({
or: [{ age: { gte: 18, lte: 30 } }, { is_blocked: { exists: false } }],
})
.sort({ age: -1 })
.fetch();
const users = await User.where({
$or: [{ age: { $gte: 18, $lte: 30 } }, { is_blocked: { $exists: false } }],
})
.sort({ age: -1 })
.fetch();
Make sure to register the lucid provider to make use of Database
and LucidMongo
models. The providers are registered inside start/app.js
const providers = [
"lucid-mongo/providers/LucidMongoProvider",
];
const aceProviders = [
"lucid-mongo/providers/MigrationsProvider",
];
the config automatic create to config/database.js
file
module.exports = {
connection: Env.get("DB_CONNECTION", "mongodb"),
mongodb: {
client: "mongodb",
connectionString: Env.get("DB_CONNECTION_STRING", ""),
connection: {
host: Env.get("DB_HOST", "localhost"),
port: Env.get("DB_PORT", 27017),
username: Env.get("DB_USER", "admin"),
password: Env.get("DB_PASSWORD", ""),
database: Env.get("DB_DATABASE", "adonis"),
options: {
},
},
},
};
Configuring Auth serializer
Edit the config/auth.js file for including the serializer. For example on the api schema
session: {
serializer: 'LucidMongo',
model: 'App/Models/User',
scheme: 'session',
uid: 'email',
password: 'password'
},
basic: {
serializer: 'LucidMongo',
model: 'App/Models/User',
scheme: 'basic',
uid: 'email',
password: 'password'
},
jwt: {
serializer: 'LucidMongo',
model: 'App/Models/User',
token: 'App/Models/Token',
scheme: 'jwt',
uid: 'email',
password: 'password',
expiry: '20m',
options: {
secret: Env.get('APP_KEY')
}
},
api: {
serializer: 'LucidMongo',
scheme: 'api',
model: 'App/Models/User',
token: 'App/Models/Token',
uid: 'username',
password: '',
expiry: '30d',
},
Use standalone (still in development)
To setup this package as standalone package
$ npm i @izorwebid/lucid-mongo-2
const config = {
connection: 'mongodb',
mongodb: {
client: 'mongodb',
connectionString: 'mongo://username:password@localhost/my_database',
connection: {
host: 'localhost',
port: 27017,
username: 'my_user',
password: 'my_password',
database: 'my_database'
options: {
}
}
}
}
const { Models, Model } = require("./")(config);
class User extends Model {}
Models.add("App/Model/User", User);
module.exports = User;
async function test() {
const users = await User.where({ isActive: false }).fetch();
console.log(users.toJSON());
}
test();
Query
const users = await User.all();
const users = await User.where("name", "peter").fetch();
const users = await User.where({ name: "peter" }).limit(10).skip(20).fetch();
const users = await User.where({
$or: [
{ gender: "female", age: { $gte: 20 } },
{ gender: "male", age: { $gte: 22 } },
],
}).fetch();
const user = await User.where("name")
.eq("peter")
.where("age")
.gt(18)
.lte(60)
.sort("-age")
.first();
const users = await User.where({ age: { $gte: 18 } })
.sort({ age: -1 })
.fetch();
const users = await User.where("age", ">=", 18).fetch();
const users = await User.where("age").gt(18).paginate(2, 100);
const users = await User.where(function () {
this.where("age", ">=", 18);
}).fetch();
const images = await Image.where(location)
.near({ center: [1, 1] })
.maxDistance(5000)
.fetch();
const images = await Image.where(location)
.near({ center: [1, 1], sphere: true })
.maxDistance(5000)
.fetch();
More Documentation of mquery
Aggregation
const count = await Customer.count();
const count_rows = await Customer.where({ invited: { $exist: true } }).count(
"position"
);
const max = await Employee.max("age");
const total_rows = await Employee.where(active, true).sum(
"salary",
"department_id"
);
const avg_rows = await Employee.where(active, true).avg("salary", {
department: "$department_id",
role: "$role_id",
});
Relations
This package support relations like adonis-lucid:
- hasOne
- belongsTo
- hasMany
- hasManyThrough
- belongsToMany
More Documentation of adonis relationships
mongodb has no join query so this package has no query like: has
, whereHas
, doesntHave
, whereDoesntHave
Addition relations
morphMany:
A model can belong to more than one other model, on a single association. For example, you might have a Picture model that belongs to either an Author model or a Reader model
class Author extends Model {
pictures() {
return this.morphMany(
"App/Model/Picture",
"pictureableType",
"pictureableId"
);
}
}
class Reader extends Model {
pictures() {
return this.morphMany(
"App/Model/Picture",
"pictureableType",
"pictureableId"
);
}
}
class Picture extends Model {
imageable() {
return this.morphTo("App/Model", "pictureable_type", "pictureable_id");
}
}
embedsOne:
EmbedsOne is used to represent a model that embeds another model, for example, a Customer embeds one billingAddress.
class Customer extends Model {
billingAddress() {
return this.embedsOne("App/Model/Address", "_id", "billingAddress");
}
}
embedsMany:
Use an embedsMany relation to indicate that a model can embed many instances of another model. For example, a Customer can have multiple email addresses and each email address is a complex object that contains label and address.
class Customer extends Model {
emails() {
return this.embedsMany("App/Model/Email", "_id", "emails");
}
}
referMany:
Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s)
class Bill extends Model {
items() {
return this.referMany("App/Model/Item", "_id", "items");
}
}
Query relationships
const users = await User.with("emails").fetch();
const user = await User.with("emails", (query) => {
query.where({ status: "verified" });
}).first();
const user = await User.with(["emails", "phones"]).first();
const user = await User.with({
emails: {
where: { verified: true },
sort: "-created_at",
},
}).first();
const user = await User.with({
emails: (query) => {
query.where("active", true);
},
}).first();
Query logging
To show query logs run this command:
- Linux, MacOS
DEBUG=mquery npm run dev
- Windows
setx DEBUG mquery && npm run dev
Migration
Current only support create, drop, rename collection and index
up () {
this.create('articles', (collection) => {
collection.index('title_index', {title: 1})
})
this.collection('users', (collection) => {
collection.index('email_index', {email: 1}, {unique: true})
})
this.collection('image', (collection) => {
collection.index('location_index', {location: '2dsphere'}, {'2dsphereIndexVersion': 2})
})
this.rename('articles', 'posts')
this.create('posts', (collection) => {
collection.dropIndex('title_index')
})
this.drop('articles', 'posts')
}
Field type
Type of mongodb.ObjectID
The objectId fields will be converted to mongodb.ObjectID before save to db.
class Article extends LucidMongo {
static get objectIDs() {
return ["_id", "categoryId"];
}
}
The where query conditions will be converted to objectId too
const article = await Article.find("58ccb403f895502b84582c63");
const articles = await Article.where({
department_id: "58ccb403f895502b84582c63",
}).fetch();
Type of date
class Staff extends LucidMongo {
static get dates() {
return ["dob"];
}
}
The field declare as date will be converted to moment js object after get from db
const staff = await Staff.first();
const yearAgo = staff.dob.fromNow();
You can set attribute of model as moment|Date|string, this field will be converted to date before save to db
staff.dob = moment(request.input("dob"));
The where query conditions will be converted to date too
const user = await User.where({ created_at: { $gte: "2017-01-01" } }).fetch();
Date type is UTC timezone
Type of geometry
class Image extends LucidMongo {
static get geometries() {
return ["location"];
}
}
When declare field type as geometry the field will be transformed to geoJSON type
const image = await Image.create({
fileName: fileName,
location: {
latitude: 1,
longitude: 2,
},
});
Result:
{ "type": "Point", "coordinates": [2, 1] }
After get from db it will be retransformed to
{
latitude: 1,
longitude: 2
}
Use mquery builder
const Database = use("Database");
const db = await Database.connect("mongodb");
const users = await db.collection("users").find();
const phone = await db
.collection("phones")
.where({ userId: ObjectID("58ccb403f895502b84582c63") })
.findOne();
const count = await db.collection("user").where({ active: true }).count();
Get mongodb client object
In case the query builder does not match your requirement you can get mongodbClient to do your custom query
const Database = use("Database");
const mongoClient = await Database.connect();
const result = await mongoClient
.collection("inventory")
.find({ size: { h: 14, w: 21, uom: "cm" } })
.toArray();
Contribution Guidelines
In favor of active development we accept contributions for everyone. You can contribute by submitting a bug, creating pull requests or even improving documentation.
License