@adonisjs/lucid
Version:
SQL ORM built on top of Active Record pattern
122 lines (121 loc) • 4.38 kB
JavaScript
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { getValue, unique } from '../../../utils/index.js';
import { BaseQueryBuilder } from '../base/query_builder.js';
/**
* Extends the model query builder for executing queries in scope
* to the current relationship
*/
export class HasManyQueryBuilder extends BaseQueryBuilder {
parent;
relation;
appliedConstraints = false;
constructor(builder, client, parent, relation) {
super(builder, client, relation, (userFn) => {
return ($builder) => {
const subQuery = new HasManyQueryBuilder($builder, this.client, this.parent, this.relation);
subQuery.isChildQuery = true;
subQuery.isRelatedPreloadQuery = this.isRelatedPreloadQuery;
userFn(subQuery);
subQuery.applyWhere();
};
});
this.parent = parent;
this.relation = relation;
}
/**
* Profiler data for HasMany relationship
*/
profilerData() {
return {
type: this.relation.type,
model: this.relation.model.name,
relatedModel: this.relation.relatedModel().name,
};
}
/**
* The keys for constructing the join query
*/
getRelationKeys() {
return [this.relation.foreignKey];
}
/**
* Clones the current query
*/
clone() {
const clonedQuery = new HasManyQueryBuilder(this.knexQuery.clone(), this.client, this.parent, this.relation);
this.applyQueryFlags(clonedQuery);
clonedQuery.appliedConstraints = this.appliedConstraints;
clonedQuery.isRelatedPreloadQuery = this.isRelatedPreloadQuery;
clonedQuery.debug(this.debugQueries);
clonedQuery.reporterData(this.customReporterData);
return clonedQuery;
}
/**
* Applies constraint to limit rows to the current relationship
* only.
*/
applyConstraints() {
if (this.appliedConstraints) {
return;
}
const queryAction = this.queryAction();
this.appliedConstraints = true;
/**
* Eager query constraints
*/
if (Array.isArray(this.parent)) {
this.wrapExisting().whereIn(this.relation.foreignKey, unique(this.parent.map((model) => {
return getValue(model, this.relation.localKey, this.relation, queryAction);
})));
return;
}
/**
* Query constraints
*/
const value = getValue(this.parent, this.relation.localKey, this.relation, queryAction);
this.wrapExisting().where(this.relation.foreignKey, value);
}
/**
* Same as standard model query builder paginate method. But ensures that
* it is not invoked during eagerloading
*/
paginate(page, perPage = 20) {
if (this.isRelatedPreloadQuery) {
throw new Error(`Cannot paginate relationship "${this.relation.relationName}" during preload`);
}
this.applyConstraints();
return super.paginate(page, perPage);
}
/**
* Returns the group limit query
*/
getGroupLimitQuery() {
const { direction, column } = this.groupConstraints.orderBy || {
column: this.resolveKey(this.relation.relatedModel().primaryKey),
direction: 'desc',
};
const rowName = 'adonis_group_limit_counter';
const partitionBy = `PARTITION BY ${this.relation.foreignKeyColumnName}`;
const orderBy = `ORDER BY ${column} ${direction}`;
/**
* Select * when no columns are selected
*/
if (!this.getSelectedColumns()) {
this.select('*');
}
this.select(this.client.raw(`row_number() over (${partitionBy} ${orderBy}) as ${rowName}`)).as('adonis_temp');
const groupQuery = this.relation.relatedModel().query();
groupQuery.usePreloader(this.preloader);
groupQuery.sideload(this.sideloaded);
groupQuery.debug(this.debugQueries);
this.customReporterData && groupQuery.reporterData(this.customReporterData);
return groupQuery.from(this).where(rowName, '<=', this.groupConstraints.limit);
}
}