npm package: @mqict/adonis-model
Description: MQ-Models is a set of base models for MQ's Team server. MQ-Models runs on **Adonisjs 4**, **lucid-mongo**. It rewrites the relational definitions in models and functions to extend and query the mongoDB more easily. Lucid mongo: https://github.com/duyluong
Author: NgocDD
Latest version: 1.0.2

# Welcome to MQ Adonisjs Model!
MQ-Models is a set of base models for MQ's Team server.
MQ-Models runs on **Adonisjs 4**, **lucid-mongo**.
It rewrites the relational definitions in models and functions to extend and query the mongoDB more easily.
Lucid mongo: https://github.com/duyluonglc/lucid-mongo

# Install
npm install @mqict/adonis-model

in Models file: use use('@mqict/adonis-model') as BaseModel.
```javascript
	const Model =  use('@mqict/adonis-model')
    class  User  extends  Model {
    	.....
    }
```	

in start/app.js add command:
```javascript
...
/*
|--------------------------------------------------------------------------
| Commands
|--------------------------------------------------------------------------
|
| Here you store ace commands for your package
|
*/
const commands = [
  '@mqict/adonis-model/Commands/commands.js'
]
....
```
# Using

### Relations
This package support relations like lucid-mongo:

 - belongsTo 
 - belongsToMany 
 - hasMany 
 - hasManyThrough 
 - hasOne 
 - morphMany
 - morphTo 
 - morphOne  
 - embedsOne 
 - embedsMany 
 - referMany
#### Addition relations
Use relationships to declare relationships, which are structured as:
```javascript
    static get relationships() {
	    return {
		    <relationName1>:{
			    <relatedName1>: [<Model Related 1>, <localField>, <ForeginField>],
			    <relatedName2>: [<Model Related 2>, <localField>, <ForeginField>],
		    },
		    <relationName2>:{
			    <relatedName3>: [<Model Related 3>, <localField>, <ForeginField>],
			    <relatedName4>: [<Model Related 4>, <localField>, <ForeginField>, <custom params (optional)>],
		    },
	    }
    }
```
Relations between tables will be created automatically, but in the case of too complex data, it is necessary to declare custom params to create data constraints.
Example:

```javascript
    const Model =  use('@mqict/adonis-model')
    class  User  extends  Model {
	    static  get relationships() {
		    return {
				hasMany: {
					tokens: ['App/Models/Token', '_id', 'userId']
				},
				referMany: {
					roles: ['App/Models/Role', '_id', 'roleIds'],
					permissions: ['App/Models/Permission', '_id', 'permissionIds', {
            $lookup: {
              from: 'permissions',
              let : {
                localField: `$roles.permisstions`
              },
              pipeline: [
                {$match:{
                  $expr: {$in: ["$_id", "$$localField"]}
                }}
              ],
              as: 'permissions'
            }
            }]},
				}
			}
		}
		.....
	}

```
### Schema
Schema is the structure of the database. It is used to query more easily.
The library automatically generates schemas or you can create them manually.
Automatic creation using command:
```javascript
adonis mqmodel schema: create
```

To be able to create automatically, you need to create all the tables and insert into each table at least one record template.

Handmade:
Create the folder app / Models / Schemas /
In this folder, create the files corresponding to each model. Its format is json.
"Field Name": "Data type"
For example:
User.json
```javascript
{
    "_id": "objectid",
    "account": "string",
    "name": "string",
    "password": "string",
    "roleIds": "array",
    "permissionIds": "array",
    "created_at": "moment",
    "updated_at": "moment",
    "roles": "function",
    "permissions": "function",
    "tokens": "function"
}
```
Acceptable data types:
string
number
array
object
moment
objectid
string


### Query
Same as Lucid-mongo & mquery:
```javascript
        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()
    
    // to query geo near you need add 2d or 2dsphere index in migration file
	    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](https://github.com/aheckmann/mquery)
    
    ### [](https://github.com/duyluonglc/lucid-mongo#aggregation)Aggregation
    
      // count without group by
      const count = await Customer.count()
    
      // count group by `position`
      const count_rows = await Customer
        .where({ invited: { $exist: true } })
        .count('position')
    
      // max age without group by
      const max = await Employee.max('age')
    
      // sum `salary` group by `department_id`
      const total_rows = await Employee
        .where(active, true)
        .sum('salary', 'department_id')
    
      // average group by `department_id` and `role_id`
      const avg_rows = await Employee
        .where(active, true)
        .avg('salary', { department: '$department_id', role: '$role_id' })

```
## Aggregation
MQ-Models hỗ trợ aggregation.
sử dụng fetchAggregate() hoặc aggregate([....]) 

Example:
```javascript
    const users = await User.with('emails').fetchAggregate()
     const users = await User.with('emails').aggregate([
	     $match: {
		     username: /ngoc/i
	     },
	     $unwind: {
		     path: 'emails',
		     preserveNullAndEmptyArrays: true
	     }
	])
	//or
	const users = await User.with('emails').where({username: "ngoc"}).fetchAggregate()
```	
More: https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/
#### Auto unwind
By default, auto-unwind supports aggragation with dependencies: belongsTo,
  hasOne, embedsOne, morphOne.
You can add more by overriding the autoUnwind function below:
```javascript
    static  get autoUnwind(){
	    return [
		    "belongsTo",
		    "hasOne",
		    "embedsOne",
		    "morphOne"
		]
	}
```
## lookup Recursive
```javascript
Order.query.lookupRecursive({
          '_id': 1,
          'code': 1,
          'customer_name': 1,
          'type': 1,
          'status': 1,
          'OrderDetail': {
            _id: 1,
            quantity: 1,
            Product: {
              _id: 1,
              name: 1
            },
            $match: {
              quantity: {$gte: "4"}
            }
          },
          $match: {
            "OrderDetail._id" : {
              $exists: true
            }
          }
        }).fetchAggregate()
```
output:
```javascript
{
    "_id": "5bdc15838270df06b49008f8",
    "code": "123456",
    "customer_name": "日本㈱",
    "type": "0",
    "status": "1",
    "customer_code": "12345",
    "OrderDetail": [
        {
            "_id": "5bdfa2c23829550b884a6679",
            "quantity": "5",
            "Product": {
                "_id": "5bd17ece0762192320a6d634",
                "name": "L金型",
            }
        },
        {
            "_id": "5bdfa2c23829550b884a6672",
            "quantity": "5",
            "Product": {
                "_id": "5bd17ece0762192320a6d634",
                "name": "L金型",
            }
        },
        {
            "_id": "5bdfa2c23829550b884a66fd",
            "quantity": "5",
            "Product": {
                "_id": "5bd17ece0762192320a6d634",
                "name": "L金型",
            }
        },
        {
            "_id": "5bdfa2c23829550b884a6612",
            "quantity": "5",
            "Product": {
                "_id": "5bd17ece0762192320a6d634",
                "name": "L金型",
            }
        }
    ]
}
```
NgocHip