Skip to content

Commit

Permalink
feat(model): implement update,delete and counter methods to model que…
Browse files Browse the repository at this point in the history
…ry builder
  • Loading branch information
thetutlage committed Jan 12, 2020
1 parent 9b48d5f commit 912f7e1
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 26 deletions.
23 changes: 19 additions & 4 deletions adonis-typings/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@

declare module '@ioc:Adonis/Lucid/Model' {
import { ProfilerContract, ProfilerRowContract } from '@ioc:Adonis/Core/Profiler'
import { ChainableContract, StrictValues, QueryCallback } from '@ioc:Adonis/Lucid/DatabaseQueryBuilder'
import {
Update,
Counter,
StrictValues,
QueryCallback,
ChainableContract,
} from '@ioc:Adonis/Lucid/DatabaseQueryBuilder'

import {
QueryClientContract,
TransactionClientContract,
Expand Down Expand Up @@ -368,9 +375,9 @@ declare module '@ioc:Adonis/Lucid/Model' {
/**
* Model query builder will have extras methods on top of Database query builder
*/
export interface ModelQueryBuilderContract<
Model extends ModelConstructorContract
> extends ChainableContract {
export interface ModelQueryBuilderContract<Model extends ModelConstructorContract>
extends ChainableContract
{
model: Model

/**
Expand Down Expand Up @@ -405,6 +412,14 @@ declare module '@ioc:Adonis/Lucid/Model' {
*/
firstOrFail (): Promise<InstanceType<Model>>

/**
* Mutations (update and increment can be one query aswell)
*/
update: Update<ModelQueryBuilderContract<Model> & ExcutableQueryBuilderContract<number[]>>
increment: Counter<ModelQueryBuilderContract<Model> & ExcutableQueryBuilderContract<number[]>>
decrement: Counter<ModelQueryBuilderContract<Model> & ExcutableQueryBuilderContract<number[]>>
del (): ModelQueryBuilderContract<Model> & ExcutableQueryBuilderContract<number[]>

/**
* Define relationships to be preloaded
*/
Expand Down
6 changes: 3 additions & 3 deletions adonis-typings/querybuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,8 +602,6 @@ declare module '@ioc:Adonis/Lucid/DatabaseQueryBuilder' {
export interface DatabaseQueryBuilderContract <
Result extends any = Dictionary<any, string>,
> extends ChainableContract {
del (): this

client: QueryClientContract,

/**
Expand All @@ -616,8 +614,10 @@ declare module '@ioc:Adonis/Lucid/DatabaseQueryBuilder' {
*/
first (): Promise<Result | null>

del (): this

/**
* Mutations
* Mutations (update and increment can be one query aswell)
*/
update: Update<this>
increment: Counter<this>
Expand Down
18 changes: 0 additions & 18 deletions src/Database/QueryBuilder/Chainable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1095,22 +1095,4 @@ export abstract class Chainable implements ChainableContract {
this.$knexBuilder.sum(this._normalizeAggregateColumns(columns, alias))
return this
}

/**
* Perform update by incrementing value for a given column. Increments
* can be clubbed with `update` as well
*/
public increment (column: any, counter?: any): this {
this.$knexBuilder.increment(column, counter)
return this
}

/**
* Perform update by decrementing value for a given column. Decrements
* can be clubbed with `update` as well
*/
public decrement (column: any, counter?: any): this {
this.$knexBuilder.decrement(column, counter)
return this
}
}
18 changes: 18 additions & 0 deletions src/Database/QueryBuilder/Database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,24 @@ export class DatabaseQueryBuilder extends Chainable implements DatabaseQueryBuil
return this
}

/**
* Perform update by incrementing value for a given column. Increments
* can be clubbed with `update` as well
*/
public increment (column: any, counter?: any): this {
this.$knexBuilder.increment(column, counter)
return this
}

/**
* Perform update by decrementing value for a given column. Decrements
* can be clubbed with `update` as well
*/
public decrement (column: any, counter?: any): this {
this.$knexBuilder.decrement(column, counter)
return this
}

/**
* Perform update
*/
Expand Down
75 changes: 75 additions & 0 deletions src/Orm/QueryBuilder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ import { Executable, ExecutableConstructor } from '../../Traits/Executable'
@trait<ExecutableConstructor>(Executable)
export class ModelQueryBuilder extends Chainable implements ModelQueryBuilderContract<ModelConstructorContract
> {
/**
* A flag to know, if the query being executed is a select query
* or not, since we don't transform return types of non-select
* queries
*/
private _isSelectQuery: boolean = true

/**
* Sideloaded attributes that will be passed to the model instances
*/
Expand Down Expand Up @@ -67,6 +74,16 @@ export class ModelQueryBuilder extends Chainable implements ModelQueryBuilderCon
builder.table(model.$table)
}

/**
* Ensures that we are not executing `update` or `del` when using read only
* client
*/
private _ensureCanPerformWrites () {
if (this.client && this.client.mode === 'read') {
throw new Exception('Updates and deletes cannot be performed in read mode')
}
}

/**
* Process preloads for a single model instance
*/
Expand All @@ -85,11 +102,24 @@ export class ModelQueryBuilder extends Chainable implements ModelQueryBuilderCon
return modelInstances
}

/**
* Checks to see that the executed query is update or delete
*/
public async beforeExecute () {
if (['update', 'del'].includes(this.$knexBuilder['_method'])) {
this._isSelectQuery = false
}
}

/**
* Wraps the query result to model instances. This method is invoked by the
* Executable trait.
*/
public async afterExecute (rows: any[]): Promise<any[]> {
if (!this._isSelectQuery) {
return Array.isArray(rows) ? rows : [rows]
}

const modelInstances = this.model.$createMultipleFromAdapterResult(
rows,
this.$sideloaded,
Expand Down Expand Up @@ -149,6 +179,14 @@ export class ModelQueryBuilder extends Chainable implements ModelQueryBuilderCon
* to running the query
*/
public getQueryClient () {
/**
* Use write client for updates and deletes
*/
if (['update', 'del'].includes(this.$knexBuilder['_method'])) {
this._ensureCanPerformWrites()
return this.client!.getWriteClient().client
}

return this.client!.getReadClient().client
}

Expand All @@ -166,4 +204,41 @@ export class ModelQueryBuilder extends Chainable implements ModelQueryBuilderCon
model: this.model.name,
}))
}
/**
* Perform update by incrementing value for a given column. Increments
* can be clubbed with `update` as well
*/
public increment (column: any, counter?: any): any {
this._ensureCanPerformWrites()
this.$knexBuilder.increment(column, counter)
return this
}

/**
* Perform update by decrementing value for a given column. Decrements
* can be clubbed with `update` as well
*/
public decrement (column: any, counter?: any): any {
this._ensureCanPerformWrites()
this.$knexBuilder.decrement(column, counter)
return this
}

/**
* Perform update
*/
public update (columns: any): any {
this._ensureCanPerformWrites()
this.$knexBuilder.update(columns)
return this
}

/**
* Delete rows under the current query
*/
public del (): any {
this._ensureCanPerformWrites()
this.$knexBuilder.del()
return this
}
}
2 changes: 1 addition & 1 deletion src/Orm/Relations/Base/QueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export abstract class BaseRelationQueryBuilder
/**
* Adds neccessary where clause to the query to perform the select
*/
public beforeExecute () {
public async beforeExecute () {
this.applyConstraints()
}

Expand Down
1 change: 1 addition & 0 deletions test-helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export async function setup () {
table.integer('country_id')
table.string('username').unique()
table.string('email')
table.integer('points').defaultTo(0)
table.timestamps()
})
}
Expand Down
Binary file removed test-helpers/tmp/db.sqlite
Binary file not shown.
80 changes: 80 additions & 0 deletions test/orm/model-query-builder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,84 @@ test.group('Model query builder', (group) => {
assert.deepEqual(users[0].$attributes, { id: 1, username: 'virk' })
assert.deepEqual(users[0].$options!.profiler, profiler)
})

test('perform update using model query builder', async (assert) => {
class User extends BaseModel {
@column({ primary: true })
public id: number

@column()
public username: string
}

User.$boot()
await db.insertQuery().table('users').insert([{ username: 'virk' }, { username: 'nikk' }])

const rows = await User.query().where('username', 'virk').update({ username: 'hvirk' })
assert.lengthOf(rows, 1)
assert.deepEqual(rows, [1])

const user = await db.from('users').where('username', 'hvirk').first()
assert.equal(user!.username, 'hvirk')
})

test('perform increment using model query builder', async (assert) => {
class User extends BaseModel {
@column({ primary: true })
public id: number

@column()
public username: string
}

User.$boot()
await db.insertQuery().table('users').insert([{ username: 'virk', points: 1 }])

const rows = await User.query().where('username', 'virk').increment('points', 1)
assert.lengthOf(rows, 1)
assert.deepEqual(rows, [1])

const user = await db.from('users').where('username', 'virk').first()
assert.equal(user!.points, 2)
})

test('perform decrement using model query builder', async (assert) => {
class User extends BaseModel {
@column({ primary: true })
public id: number

@column()
public username: string
}

User.$boot()
await db.insertQuery().table('users').insert([{ username: 'virk', points: 3 }])

const rows = await User.query().where('username', 'virk').decrement('points', 1)
assert.lengthOf(rows, 1)
assert.deepEqual(rows, [1])

const user = await db.from('users').where('username', 'virk').first()
assert.equal(user!.points, 2)
})

test('delete in bulk', async (assert) => {
class User extends BaseModel {
@column({ primary: true })
public id: number

@column()
public username: string
}

User.$boot()
await db.insertQuery().table('users').insert([{ username: 'virk' }, { username: 'nikk' }])

const rows = await User.query().where('username', 'virk').del()
assert.lengthOf(rows, 1)
assert.deepEqual(rows, [1])

const user = await db.from('users').where('username', 'virk').first()
assert.isNull(user)
})
})

0 comments on commit 912f7e1

Please sign in to comment.