mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-24 23:48:13 -05:00
Extracted Bookshelf raw_knex
function to plugin
no issue - this commit extracts the niche `raw_knex` function from the Base model into its own plugin
This commit is contained in:
parent
6ce1b11a15
commit
f4f31027b7
3 changed files with 207 additions and 199 deletions
|
@ -48,6 +48,8 @@ ghostBookshelf.plugin(require('./actions'));
|
|||
|
||||
ghostBookshelf.plugin(require('./events'));
|
||||
|
||||
ghostBookshelf.plugin(require('./raw-knex'));
|
||||
|
||||
// Manages nested updates (relationships)
|
||||
ghostBookshelf.plugin('bookshelf-relations', {
|
||||
allowedOptions: ['context', 'importing', 'migrating'],
|
||||
|
|
|
@ -9,16 +9,12 @@
|
|||
const _ = require('lodash');
|
||||
|
||||
const moment = require('moment');
|
||||
const Promise = require('bluebird');
|
||||
const ObjectId = require('bson-objectid');
|
||||
const debug = require('@tryghost/debug')('models:base');
|
||||
const db = require('../../data/db');
|
||||
const errors = require('@tryghost/errors');
|
||||
const security = require('@tryghost/security');
|
||||
const schema = require('../../data/schema');
|
||||
const urlUtils = require('../../../shared/url-utils');
|
||||
const bulkOperations = require('./bulk-operations');
|
||||
const plugins = require('@tryghost/bookshelf-plugins');
|
||||
const tpl = require('@tryghost/tpl');
|
||||
|
||||
const ghostBookshelf = require('./bookshelf');
|
||||
|
@ -631,201 +627,6 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
|
||||
// Test for duplicate slugs.
|
||||
return checkIfSlugExists(slug);
|
||||
},
|
||||
|
||||
/**
|
||||
* If you want to fetch all data fast, i recommend using this function.
|
||||
* Bookshelf is just too slow, too much ORM overhead.
|
||||
*
|
||||
* If we e.g. instantiate for each object a model, it takes twice long.
|
||||
*/
|
||||
raw_knex: {
|
||||
fetchAll: function (options) {
|
||||
options = options || {};
|
||||
|
||||
const nql = require('@nexes/nql');
|
||||
const modelName = options.modelName;
|
||||
const tableNames = {
|
||||
Post: 'posts',
|
||||
User: 'users',
|
||||
Tag: 'tags'
|
||||
};
|
||||
const exclude = options.exclude;
|
||||
const filter = options.filter;
|
||||
const shouldHavePosts = options.shouldHavePosts;
|
||||
const withRelated = options.withRelated;
|
||||
const withRelatedFields = options.withRelatedFields;
|
||||
const relations = {
|
||||
tags: {
|
||||
targetTable: 'tags',
|
||||
name: 'tags',
|
||||
innerJoin: {
|
||||
relation: 'posts_tags',
|
||||
condition: ['posts_tags.tag_id', '=', 'tags.id']
|
||||
},
|
||||
select: ['posts_tags.post_id as post_id', 'tags.visibility'],
|
||||
whereIn: 'posts_tags.post_id',
|
||||
whereInKey: 'post_id',
|
||||
orderBy: 'sort_order'
|
||||
},
|
||||
authors: {
|
||||
targetTable: 'users',
|
||||
name: 'authors',
|
||||
innerJoin: {
|
||||
relation: 'posts_authors',
|
||||
condition: ['posts_authors.author_id', '=', 'users.id']
|
||||
},
|
||||
select: ['posts_authors.post_id as post_id'],
|
||||
whereIn: 'posts_authors.post_id',
|
||||
whereInKey: 'post_id',
|
||||
orderBy: 'sort_order'
|
||||
}
|
||||
};
|
||||
|
||||
let query = ghostBookshelf.knex(tableNames[modelName]);
|
||||
|
||||
if (options.offset) {
|
||||
query.offset(options.offset);
|
||||
}
|
||||
|
||||
if (options.limit) {
|
||||
query.limit(options.limit);
|
||||
}
|
||||
|
||||
// exclude fields if enabled
|
||||
if (exclude) {
|
||||
let toSelect = _.keys(schema.tables[tableNames[modelName]]);
|
||||
toSelect = toSelect.filter(key => !(key.startsWith('@@')));
|
||||
|
||||
_.each(exclude, (key) => {
|
||||
if (toSelect.indexOf(key) !== -1) {
|
||||
toSelect.splice(toSelect.indexOf(key), 1);
|
||||
}
|
||||
});
|
||||
|
||||
query.select(toSelect);
|
||||
}
|
||||
|
||||
// @NOTE: We can't use the filter plugin, because we are not using bookshelf.
|
||||
nql(filter).querySQL(query);
|
||||
|
||||
if (shouldHavePosts) {
|
||||
plugins.hasPosts.addHasPostsWhere(tableNames[modelName], shouldHavePosts)(query);
|
||||
}
|
||||
|
||||
if (options.id) {
|
||||
query.where({id: options.id});
|
||||
}
|
||||
|
||||
return query.then((objects) => {
|
||||
debug('fetched', modelName, filter);
|
||||
|
||||
if (!objects.length) {
|
||||
debug('No more entries found');
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
let props = {};
|
||||
|
||||
if (!withRelated) {
|
||||
return _.map(objects, (object) => {
|
||||
object = ghostBookshelf._models[modelName].prototype.toJSON.bind({
|
||||
attributes: object,
|
||||
related: function (key) {
|
||||
return object[key];
|
||||
},
|
||||
serialize: ghostBookshelf._models[modelName].prototype.serialize,
|
||||
formatsToJSON: ghostBookshelf._models[modelName].prototype.formatsToJSON
|
||||
})();
|
||||
|
||||
object = ghostBookshelf._models[modelName].prototype.fixBools(object);
|
||||
object = ghostBookshelf._models[modelName].prototype.fixDatesWhenFetch(object);
|
||||
return object;
|
||||
});
|
||||
}
|
||||
|
||||
_.each(withRelated, (withRelatedKey) => {
|
||||
const relation = relations[withRelatedKey];
|
||||
|
||||
props[relation.name] = (() => {
|
||||
debug('fetch withRelated', relation.name);
|
||||
|
||||
let relationQuery = db.knex(relation.targetTable);
|
||||
|
||||
// default fields to select
|
||||
_.each(relation.select, (fieldToSelect) => {
|
||||
relationQuery.select(fieldToSelect);
|
||||
});
|
||||
|
||||
// custom fields to select
|
||||
_.each(withRelatedFields[withRelatedKey], (toSelect) => {
|
||||
relationQuery.select(toSelect);
|
||||
});
|
||||
|
||||
relationQuery.innerJoin(
|
||||
relation.innerJoin.relation,
|
||||
relation.innerJoin.condition[0],
|
||||
relation.innerJoin.condition[1],
|
||||
relation.innerJoin.condition[2]
|
||||
);
|
||||
|
||||
relationQuery.whereIn(relation.whereIn, _.map(objects, 'id'));
|
||||
relationQuery.orderBy(relation.orderBy);
|
||||
|
||||
return relationQuery
|
||||
.then((queryRelations) => {
|
||||
debug('fetched withRelated', relation.name);
|
||||
|
||||
// arr => obj[post_id] = [...] (faster access)
|
||||
return queryRelations.reduce((obj, item) => {
|
||||
if (!obj[item[relation.whereInKey]]) {
|
||||
obj[item[relation.whereInKey]] = [];
|
||||
}
|
||||
|
||||
obj[item[relation.whereInKey]].push(_.omit(item, relation.select));
|
||||
return obj;
|
||||
}, {});
|
||||
});
|
||||
})();
|
||||
});
|
||||
|
||||
return Promise.props(props)
|
||||
.then((relationsToAttach) => {
|
||||
debug('attach relations', modelName);
|
||||
|
||||
objects = _.map(objects, (object) => {
|
||||
_.each(Object.keys(relationsToAttach), (relation) => {
|
||||
if (!relationsToAttach[relation][object.id]) {
|
||||
object[relation] = [];
|
||||
return;
|
||||
}
|
||||
|
||||
object[relation] = relationsToAttach[relation][object.id];
|
||||
});
|
||||
|
||||
object = ghostBookshelf._models[modelName].prototype.toJSON.bind({
|
||||
attributes: object,
|
||||
_originalOptions: {
|
||||
withRelated: Object.keys(relationsToAttach)
|
||||
},
|
||||
related: function (key) {
|
||||
return object[key];
|
||||
},
|
||||
serialize: ghostBookshelf._models[modelName].prototype.serialize,
|
||||
formatsToJSON: ghostBookshelf._models[modelName].prototype.formatsToJSON
|
||||
})();
|
||||
|
||||
object = ghostBookshelf._models[modelName].prototype.fixBools(object);
|
||||
object = ghostBookshelf._models[modelName].prototype.fixDatesWhenFetch(object);
|
||||
return object;
|
||||
});
|
||||
|
||||
debug('attached relations', modelName);
|
||||
|
||||
return objects;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
205
core/server/models/base/raw-knex.js
Normal file
205
core/server/models/base/raw-knex.js
Normal file
|
@ -0,0 +1,205 @@
|
|||
const _ = require('lodash');
|
||||
const debug = require('@tryghost/debug')('models:base:raw-knex');
|
||||
const plugins = require('@tryghost/bookshelf-plugins');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
const schema = require('../../data/schema');
|
||||
|
||||
module.exports = function (Bookshelf) {
|
||||
Bookshelf.Model = Bookshelf.Model.extend({}, {
|
||||
/**
|
||||
* If you want to fetch all data fast, i recommend using this function.
|
||||
* Bookshelf is just too slow, too much ORM overhead.
|
||||
*
|
||||
* If we e.g. instantiate for each object a model, it takes twice long.
|
||||
*/
|
||||
raw_knex: {
|
||||
fetchAll: function (options) {
|
||||
options = options || {};
|
||||
|
||||
const nql = require('@nexes/nql');
|
||||
const modelName = options.modelName;
|
||||
const tableNames = {
|
||||
Post: 'posts',
|
||||
User: 'users',
|
||||
Tag: 'tags'
|
||||
};
|
||||
const exclude = options.exclude;
|
||||
const filter = options.filter;
|
||||
const shouldHavePosts = options.shouldHavePosts;
|
||||
const withRelated = options.withRelated;
|
||||
const withRelatedFields = options.withRelatedFields;
|
||||
const relations = {
|
||||
tags: {
|
||||
targetTable: 'tags',
|
||||
name: 'tags',
|
||||
innerJoin: {
|
||||
relation: 'posts_tags',
|
||||
condition: ['posts_tags.tag_id', '=', 'tags.id']
|
||||
},
|
||||
select: ['posts_tags.post_id as post_id', 'tags.visibility'],
|
||||
whereIn: 'posts_tags.post_id',
|
||||
whereInKey: 'post_id',
|
||||
orderBy: 'sort_order'
|
||||
},
|
||||
authors: {
|
||||
targetTable: 'users',
|
||||
name: 'authors',
|
||||
innerJoin: {
|
||||
relation: 'posts_authors',
|
||||
condition: ['posts_authors.author_id', '=', 'users.id']
|
||||
},
|
||||
select: ['posts_authors.post_id as post_id'],
|
||||
whereIn: 'posts_authors.post_id',
|
||||
whereInKey: 'post_id',
|
||||
orderBy: 'sort_order'
|
||||
}
|
||||
};
|
||||
|
||||
let query = Bookshelf.knex(tableNames[modelName]);
|
||||
|
||||
if (options.offset) {
|
||||
query.offset(options.offset);
|
||||
}
|
||||
|
||||
if (options.limit) {
|
||||
query.limit(options.limit);
|
||||
}
|
||||
|
||||
// exclude fields if enabled
|
||||
if (exclude) {
|
||||
let toSelect = _.keys(schema.tables[tableNames[modelName]]);
|
||||
toSelect = toSelect.filter(key => !(key.startsWith('@@')));
|
||||
|
||||
_.each(exclude, (key) => {
|
||||
if (toSelect.indexOf(key) !== -1) {
|
||||
toSelect.splice(toSelect.indexOf(key), 1);
|
||||
}
|
||||
});
|
||||
|
||||
query.select(toSelect);
|
||||
}
|
||||
|
||||
// @NOTE: We can't use the filter plugin, because we are not using bookshelf.
|
||||
nql(filter).querySQL(query);
|
||||
|
||||
if (shouldHavePosts) {
|
||||
plugins.hasPosts.addHasPostsWhere(tableNames[modelName], shouldHavePosts)(query);
|
||||
}
|
||||
|
||||
if (options.id) {
|
||||
query.where({id: options.id});
|
||||
}
|
||||
|
||||
return query.then((objects) => {
|
||||
debug('fetched', modelName, filter);
|
||||
|
||||
if (!objects.length) {
|
||||
debug('No more entries found');
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
let props = {};
|
||||
|
||||
if (!withRelated) {
|
||||
return _.map(objects, (object) => {
|
||||
object = Bookshelf._models[modelName].prototype.toJSON.bind({
|
||||
attributes: object,
|
||||
related: function (key) {
|
||||
return object[key];
|
||||
},
|
||||
serialize: Bookshelf._models[modelName].prototype.serialize,
|
||||
formatsToJSON: Bookshelf._models[modelName].prototype.formatsToJSON
|
||||
})();
|
||||
|
||||
object = Bookshelf._models[modelName].prototype.fixBools(object);
|
||||
object = Bookshelf._models[modelName].prototype.fixDatesWhenFetch(object);
|
||||
return object;
|
||||
});
|
||||
}
|
||||
|
||||
_.each(withRelated, (withRelatedKey) => {
|
||||
const relation = relations[withRelatedKey];
|
||||
|
||||
props[relation.name] = (() => {
|
||||
debug('fetch withRelated', relation.name);
|
||||
|
||||
let relationQuery = Bookshelf.knex(relation.targetTable);
|
||||
|
||||
// default fields to select
|
||||
_.each(relation.select, (fieldToSelect) => {
|
||||
relationQuery.select(fieldToSelect);
|
||||
});
|
||||
|
||||
// custom fields to select
|
||||
_.each(withRelatedFields[withRelatedKey], (toSelect) => {
|
||||
relationQuery.select(toSelect);
|
||||
});
|
||||
|
||||
relationQuery.innerJoin(
|
||||
relation.innerJoin.relation,
|
||||
relation.innerJoin.condition[0],
|
||||
relation.innerJoin.condition[1],
|
||||
relation.innerJoin.condition[2]
|
||||
);
|
||||
|
||||
relationQuery.whereIn(relation.whereIn, _.map(objects, 'id'));
|
||||
relationQuery.orderBy(relation.orderBy);
|
||||
|
||||
return relationQuery
|
||||
.then((queryRelations) => {
|
||||
debug('fetched withRelated', relation.name);
|
||||
|
||||
// arr => obj[post_id] = [...] (faster access)
|
||||
return queryRelations.reduce((obj, item) => {
|
||||
if (!obj[item[relation.whereInKey]]) {
|
||||
obj[item[relation.whereInKey]] = [];
|
||||
}
|
||||
|
||||
obj[item[relation.whereInKey]].push(_.omit(item, relation.select));
|
||||
return obj;
|
||||
}, {});
|
||||
});
|
||||
})();
|
||||
});
|
||||
|
||||
return Promise.props(props)
|
||||
.then((relationsToAttach) => {
|
||||
debug('attach relations', modelName);
|
||||
|
||||
objects = _.map(objects, (object) => {
|
||||
_.each(Object.keys(relationsToAttach), (relation) => {
|
||||
if (!relationsToAttach[relation][object.id]) {
|
||||
object[relation] = [];
|
||||
return;
|
||||
}
|
||||
|
||||
object[relation] = relationsToAttach[relation][object.id];
|
||||
});
|
||||
|
||||
object = Bookshelf._models[modelName].prototype.toJSON.bind({
|
||||
attributes: object,
|
||||
_originalOptions: {
|
||||
withRelated: Object.keys(relationsToAttach)
|
||||
},
|
||||
related: function (key) {
|
||||
return object[key];
|
||||
},
|
||||
serialize: Bookshelf._models[modelName].prototype.serialize,
|
||||
formatsToJSON: Bookshelf._models[modelName].prototype.formatsToJSON
|
||||
})();
|
||||
|
||||
object = Bookshelf._models[modelName].prototype.fixBools(object);
|
||||
object = Bookshelf._models[modelName].prototype.fixDatesWhenFetch(object);
|
||||
return object;
|
||||
});
|
||||
|
||||
debug('attached relations', modelName);
|
||||
|
||||
return objects;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
Loading…
Add table
Reference in a new issue