Skip to content

Commit 63a64c4

Browse files
committed
EagerLoading
1 parent 5ecbe50 commit 63a64c4

File tree

7 files changed

+226
-157
lines changed

7 files changed

+226
-157
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,31 @@ CRUD | Vuex Only | Persist to GraphQL API
7878
**D**ELETE | dispatch('delete') | dispatch('destroy')
7979

8080

81+
## Eager Loading
82+
83+
All `belongsTo` related entities are eager loaded when fetch is called. All other related entities have to be added
84+
to a static field in the model called `eagerLoad` to have them eagerly loaded with fetch.
85+
86+
Example:
87+
88+
```javascript
89+
class User extends Model {
90+
static entity = 'users';
91+
static eagerLoad = ['posts'];
92+
93+
static fields () {
94+
return {
95+
id: this.attr(null),
96+
name: this.attr(''),
97+
98+
posts: this.hasMany(Post, 'userId')
99+
}
100+
}
101+
}
102+
```
103+
104+
105+
81106
## Schema expectations
82107

83108
This plugin has an opinion how the GraphQL API schema should look like:

dist/vuex-orm-apollo.esm.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7980,8 +7980,8 @@ var QueryBuilder = /** @class */ (function () {
79807980
/**
79817981
*
79827982
* @param {Model} model
7983-
* @param {Array<Model>} ignoreModels The models in this list are ignored (while traversing relations). Mainly for recursion
7984-
* @returns {Array<String>}
7983+
* @param {Array<Model>} ignoreModels The models in this list are ignored (while traversing relations).
7984+
* @returns {string}
79857985
*/
79867986
QueryBuilder.prototype.buildRelationsQuery = function (model, ignoreModels) {
79877987
var _this = this;
@@ -7990,12 +7990,29 @@ var QueryBuilder = /** @class */ (function () {
79907990
return '';
79917991
var relationQueries = [];
79927992
model.getRelations().forEach(function (field, name) {
7993-
if (!_this.shouldModelBeIgnored(_this.getModel(name), ignoreModels)) {
7993+
var relatedModel = _this.getModel(name);
7994+
if (_this.shouldEagerLoadRelation(model, field, relatedModel) &&
7995+
!_this.shouldModelBeIgnored(relatedModel, ignoreModels)) {
79947996
var multiple = field.constructor.name !== 'BelongsTo';
7995-
relationQueries.push(_this.buildField(name, multiple, undefined, ignoreModels));
7997+
relationQueries.push(_this.buildField(relatedModel, multiple, undefined, ignoreModels));
79967998
}
79977999
});
7998-
return relationQueries;
8000+
return relationQueries.join('\n');
8001+
};
8002+
/**
8003+
* Determines if we should eager load (means: add a query field) a related entity. belongsTo related entities
8004+
* are always eager loaded. Others can be added to the eagerLoad array of the model.
8005+
*
8006+
* @param {Model} model The base model
8007+
* @param {Field} field Relation field
8008+
* @param {Model} relatedModel Related model
8009+
* @returns {boolean}
8010+
*/
8011+
QueryBuilder.prototype.shouldEagerLoadRelation = function (model, field, relatedModel) {
8012+
if (field.constructor.name === 'BelongsTo')
8013+
return true;
8014+
var eagerLoadList = model.baseModel.eagerLoad || [];
8015+
return eagerLoadList.find(function (n) { return n === relatedModel.singularName || n === relatedModel.pluralName; }) !== undefined;
79998016
};
80008017
QueryBuilder.prototype.shouldModelBeIgnored = function (model, ignoreModels) {
80018018
return ignoreModels.find(function (m) { return m.singularName === model.singularName; }) !== undefined;
@@ -8236,7 +8253,7 @@ var VuexORMApollo = /** @class */ (function () {
82368253
name = args['mutation'];
82378254
delete args['mutation'];
82388255
model = this.getModel(state.$name);
8239-
return [2 /*return*/, this.mutate(name, args, dispatch, model)];
8256+
return [2 /*return*/, this.mutate(name, args, dispatch, model, false)];
82408257
});
82418258
});
82428259
};

src/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface Arguments extends Object {
2828

2929
export interface ORMModel {
3030
entity: string;
31+
eagerLoad: undefined | Array<string>;
3132

3233
fields (): any;
3334
dispatch (name: string, ...params: Array<any>): any;

src/queryBuilder.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -267,25 +267,43 @@ export default class QueryBuilder {
267267
/**
268268
*
269269
* @param {Model} model
270-
* @param {Array<Model>} ignoreModels The models in this list are ignored (while traversing relations). Mainly for recursion
271-
* @returns {Array<String>}
270+
* @param {Array<Model>} ignoreModels The models in this list are ignored (while traversing relations).
271+
* @returns {string}
272272
*/
273-
private buildRelationsQuery (model: (null | Model), ignoreModels: Array<Model> = []) {
273+
private buildRelationsQuery (model: (null | Model), ignoreModels: Array<Model> = []): string {
274274
if (model === null) return '';
275275

276276
const relationQueries: Array<string> = [];
277277

278278
model.getRelations().forEach((field: Field, name: string) => {
279-
if (!this.shouldModelBeIgnored(this.getModel(name), ignoreModels)) {
279+
const relatedModel: Model = this.getModel(name);
280+
281+
if (this.shouldEagerLoadRelation(model, field, relatedModel) &&
282+
!this.shouldModelBeIgnored(relatedModel, ignoreModels)) {
280283
const multiple: boolean = field.constructor.name !== 'BelongsTo';
281-
relationQueries.push(this.buildField(name, multiple, undefined, ignoreModels));
284+
relationQueries.push(this.buildField(relatedModel, multiple, undefined, ignoreModels));
282285
}
283286
});
284287

285-
return relationQueries;
288+
return relationQueries.join('\n');
289+
}
290+
291+
/**
292+
* Determines if we should eager load (means: add a query field) a related entity. belongsTo related entities
293+
* are always eager loaded. Others can be added to the eagerLoad array of the model.
294+
*
295+
* @param {Model} model The base model
296+
* @param {Field} field Relation field
297+
* @param {Model} relatedModel Related model
298+
* @returns {boolean}
299+
*/
300+
private shouldEagerLoadRelation (model: Model, field: Field, relatedModel: Model): boolean {
301+
if (field.constructor.name === 'BelongsTo') return true;
302+
const eagerLoadList: Array<String> = model.baseModel.eagerLoad || [];
303+
return eagerLoadList.find((n) => n === relatedModel.singularName || n === relatedModel.pluralName) !== undefined;
286304
}
287305

288-
private shouldModelBeIgnored (model: Model, ignoreModels: Array<Model>) {
306+
private shouldModelBeIgnored (model: Model, ignoreModels: Array<Model>): boolean {
289307
return ignoreModels.find((m) => m.singularName === model.singularName) !== undefined;
290308
}
291309
}

src/vuex-orm-apollo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export default class VuexORMApollo {
157157

158158
const model = this.getModel(state.$name);
159159

160-
return this.mutate(name, args, dispatch, model);
160+
return this.mutate(name, args, dispatch, model, false);
161161
}
162162

163163
/**

0 commit comments

Comments
 (0)