I am using sequelize: 6.9.0, sequelize-cli: ^6.3.0, express: 4.17.1, pg: 8.7.1
i have a problem when using sequelize findAndCountAll, when i using include other models it will return same data when i'm using paging.
Here is my code for House Table
index: async (req, res) => {
const { page, size, developer, city, priceone, pricetwo, project, isNew } =
req.query;
const { limit, offset } = getPagination(page, size);
try {
let filter = {};
if (developer) {
filter.developerId = developer;
}
if (city) {
filter.cityId = city;
}
if (project) {
filter.projectId = project;
}
if (isNew) {
filter.isNew = isNew;
}
if (priceone && pricetwo) {
const firstPrice = parseInt(priceone);
const secondPrice = parseInt(pricetwo);
if (firstPrice === 100000000 && secondPrice === 100000000) {
filter.price = { [Op.lte]: firstPrice };
} else if (firstPrice === 2000000000 && secondPrice === 2000000000) {
filter.price = { [Op.gte]: firstPrice };
} else {
filter.price = { [Op.between]: [firstPrice, secondPrice] };
}
}
const HousesData = await Houses.findAndCountAll({
limit,
offset,
where: filter,
attributes: [
"id",
"name",
"description",
"location",
"price",
"tanah",
"bangunan",
"lantai",
"kamar_tidur",
"kamar_mandi",
"isNew",
],
include: [
{ model: Developers, attributes: ["id", "name"] },
{ model: Cities, attributes: ["id", "name"] },
{ model: Projects, attributes: ["id", "name"] },
],
});
if (HousesData) {
const response = getPagingData(HousesData, page, limit);
res.status(200).json({
status: "success",
message: "Data Available",
data: response,
});
} else {
res.status(200).json({
status: "success",
message: "There is No Data",
data: "No Data",
});
}
} catch (error) {
console.log(error);
return next(
new HttpError(
"Something went wrong, could not get project.",
500,
error
)
);
}
}
my paging function
const getPagination = (page, size) => {
const newPage = page ? page - 1 : 0;
const limit = size ? +size : 10;
const offset = newPage != 0 ? newPage * limit : 0;
return { limit, offset };
};
const getPagingData = (data, page, limit) => {
const { count: totalItems, rows: dataRows } = data;
const currentPage = page ? +page : 1;
const totalPages = Math.ceil(totalItems / limit);
return { totalItems, totalPages, currentPage, dataRows };
};
module.exports = { getPagination, getPagingData };
Let's say i have 10 data
a,b,c,d,e,f,g,h,i,j
if i see first page
http://localhost:3006/api/v1/house?size=5&page=1
it will return a,b,c,d,e (this is right)
and if i see next page
http://localhost:3006/api/v1/house?size=5&page=2
it will return e,d,c,b,a (only reverse not showing f,g,h,i,j)
and if i see all the data it will return correct data
http://localhost:3006/api/v1/house?size=10&page=1
it will return j,i,h,g,f,e,d,c,b,a
but if i disabled
include: [
{ model: Developers, attributes: ["id", "name"] },
{ model: Cities, attributes: ["id", "name"] },
{ model: Projects, attributes: ["id", "name"] },
],
it return the right data when use paging.
my model for House is here
"use strict";
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable("Houses", {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.STRING(22),
},
name: {
type: Sequelize.STRING,
},
projectId: {
type: Sequelize.STRING(22),
onDelete: "CASCADE",
references: {
model: "Projects",
key: "id",
},
},
cityId: {
type: Sequelize.STRING(22),
onDelete: "CASCADE",
references: {
model: "Cities",
key: "id",
},
},
developerId: {
type: Sequelize.STRING(22),
onDelete: "CASCADE",
references: {
model: "Developers",
key: "id",
},
},
description: {
type: Sequelize.TEXT,
},
location: {
type: Sequelize.STRING,
},
price: {
type: Sequelize.BIGINT,
},
tanah: {
type: Sequelize.INTEGER,
},
bangunan: {
type: Sequelize.INTEGER,
},
lantai: {
type: Sequelize.INTEGER,
},
kamar_tidur: {
type: Sequelize.INTEGER,
},
kamar_mandi: {
type: Sequelize.INTEGER,
},
house_thumbnail: {
type: Sequelize.STRING,
},
isNew: {
type: Sequelize.BOOLEAN,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable("Houses");
},
};
i also use same paging method on other table. but other table works fine, only this table that got messed up.
the other table named Project function is here for reference
index: async (req, res) => {
const { page, size, developer, city, priceone, pricetwo } = req.query;
const { limit, offset } = getPagination(page, size);
try {
let filter = { haveDeveloper: true };
if (developer) {
filter.developerId = developer;
}
if (city) {
filter.cityId = city;
}
if (priceone && pricetwo) {
const firstPrice = parseInt(priceone);
const secondPrice = parseInt(pricetwo);
if (firstPrice === 100000000 && secondPrice === 100000000) {
filter.minPrice = { [Op.lte]: firstPrice };
} else if (firstPrice === 2000000000 && secondPrice === 2000000000) {
filter.minPrice = { [Op.gte]: firstPrice };
} else {
filter.minPrice = { [Op.between]: [firstPrice, secondPrice] };
}
}
const projectsData = await Projects.findAndCountAll({
limit,
offset,
where: filter,
attributes: ["id", "name", "image", "location",'minPrice'],
include: [
{ model: Cities, attributes: ["id", "name"] },
{ model: Developers, attributes: ["id", "name"] },
{ model: ProjectFacilities, attributes: ["facility"] },
],
});
if (projectsData) {
const response = getPagingData(projectsData, page, limit);
res.status(200).json({
status: "success",
message: "Data Available",
data: response,
});
} else {
res.status(200).json({
status: "success",
message: "There is No Data",
data: "No Data",
});
}
} catch (error) {
console.log(error);
return next(
new HttpError(
"Something went wrong, could not get project.",
500,
error
)
);
}
},
the Project model
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Projects extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
Projects.belongsTo(models.Developers, { foreignKey: 'developerId' })
Projects.belongsTo(models.Cities, { foreignKey: 'cityId' })
Projects.hasMany(models.ProjectFacilities, { foreignKey: 'projectId' })
Projects.hasMany(models.Houses, { foreignKey: 'projectId' })
}
};
Projects.init({
name: DataTypes.STRING,
image: DataTypes.STRING,
description: DataTypes.TEXT,
location: DataTypes.STRING,
minPrice:DataTypes.BIGINT,
haveDeveloper: DataTypes.BOOLEAN,
cityId: DataTypes.STRING,
developerId: DataTypes.INTEGER
}, {
sequelize,
modelName: 'Projects',
});
return Projects;
};
sql generated by sequelize for House page 1 with 5 data
SELECT "Houses"."id", "Houses"."name", "Houses"."description", "Houses"."location", "Houses"."price", "Houses"."tanah", "Houses"."bangunan", "Houses"."lantai", "Houses"."kamar_tidur", "Houses"."kamar_mandi", "Houses"."isNew", "Developer"."id" AS "Developer.id", "Developer"."name" AS "Developer.name", "City"."id" AS "City.id", "City"."name" AS "City.name", "Project"."id" AS "Project.id", "Project"."name" AS "Project.name" FROM "Houses" AS "Houses" LEFT OUTER JOIN "Developers" AS "Developer" ON "Houses"."developerId" = "Developer"."id" LEFT OUTER JOIN "Cities" AS "City" ON "Houses"."cityId" = "City"."id" LEFT OUTER JOIN "Projects" AS "Project" ON "Houses"."projectId" = "Project"."id" LIMIT 5 OFFSET 0;
sql generated by sequelize for Project page 1 with 5 data
SELECT "Projects".*, "City"."id" AS "City.id", "City"."name" AS "City.name", "Developer"."id" AS "Developer.id", "Developer"."name" AS "Developer.name", "ProjectFacilities"."id" AS "ProjectFacilities.id", "ProjectFacilities"."facility" AS "ProjectFacilities.facility" FROM (SELECT "Projects"."id", "Projects"."name", "Projects"."image", "Projects"."location", "Projects"."minPrice", "Projects"."cityId", "Projects"."developerId" FROM "Projects" AS "Projects" WHERE "Projects"."haveDeveloper" = true LIMIT 5 OFFSET 0) AS "Projects" LEFT OUTER JOIN "Cities" AS "City" ON "Projects"."cityId" = "City"."id" LEFT OUTER JOIN "Developers" AS "Developer" ON "Projects"."developerId" = "Developer"."id" LEFT OUTER JOIN "ProjectFacilities" AS "ProjectFacilities" ON "Projects"."id" = "ProjectFacilities"."projectId";
is there any solution for this? thank you very much for your all help and attention!
Please try thi
const getPagination = (page = 1, size = 10) => {
const offset = (page - 1) * size ;
const limit = size ;
return { limit, offset };
};
I'm trying to convert below raw query to ORM but couldn't achieve anything.
raw query
select * from "Invoices" I
LEFT JOIN "Requests" R ON R."id" = I."requestId"
LEFT JOIN "Supps" S ON S."requestId" = R."id"
where S."confirm" = 'OK'
What I've tried:
Requests.belongsTo(Invoices, { foreignKey: "id" });
Supps.belongsTo(Requests, { foreignKey: "requestId" });
Supps.hasMany(Requests, { foreignKey: "requestId" });
Invoices.hasMany(Requests, { foreignKey: "requestId" });
Invoices.findOne({
include: [{ model: Requests, required: false }, { model: Supps, required: false }],
where: { Sequelize.col("Supps.confirm"): { "OK" } }
}).then(data => {
console.log(data);
})
But this generates a very very long query with multiple sub queries and wrong data
For sure you will need to add also the sourceKey in th associations. (doc)
Supps.hasMany(Requests, { foreignKey: "requestId" ,sourceKey: '{ID}' });
Invoices.hasMany(Requests, { foreignKey: "requestId",sourceKey: '{ID}' });
Also you can add {raw:true} to get a one level json response .
Invoices.findOne({
include: [{ model: Requests, required: false }, { model: Supps, required: false }],
where: { Sequelize.col("Supps.confirm"): { "OK" } },
raw:true
}).then(data => {
console.log(data);
});
I'm writing an application that should load Accepted stories that have tasks with integer in the Time Spent field.
As I can see in some tests I made the task fields that are accessible through UserStory is: 'Tasks', 'TaskActualTotal', 'TaskEstimateTotal', 'TaskRemainingTotal' and 'TaskStatus'.
Tasks has a _ref attribute with a link to a JSON with all tasks for this story. How may I explore this since I'm using Rally API? Or Is there a better way to do this?
UPDATE:
So this is pretty much i have now.
var storiesCall = Ext.create('Rally.data.WsapiDataStore', {
model: 'UserStory',
fetch: ['Tasks']
});
storiesCall.load().then({
success: this.loadTasks,
scope: this
});
loadTasks: function(stories) {
storiesCall = _.flatten(stories);
console.log(stories.length);
_.each(stories, function(story) {
var tasks = story.get('Tasks');
if(tasks.Count > 0) {
tasks.store = story.getCollection('Tasks', {fetch: ['Name','FormattedID','Workproduct','Estimate','TimeSpent']});
console.log(tasks.store.load().deferred);
}
else{
console.log('no tasks');
}
});
}
tasks.store.load().deferred is returning the following object:
Note that we can see the task data on value[0] but when i try to wrap it out with tasks.store.load().deferred.value[0] its crashing.
Any thoughts?
Per WS API doc, TimeSpent field on Task object (which is populated automatically from entries in Rally Timesheet/Time Tracker module) cannot be used in queries, so something like this (TimeSpent > 0) will not work.
Also, a UserStory object (referred to as HierarchicalRequirement in WS API) does not have a field where child tasks' TimeSpent rolls up to the story similar to how child tasks' Estimate rolls up to TaskEstimateTotal on a story.
It is possible to get TimeSpent for each task and then add them up by accessing a story's Tasks collection as done in this app:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
var initiatives = Ext.create('Rally.data.wsapi.Store', {
model: 'PortfolioItem/Initiative',
fetch: ['Children']
});
initiatives.load().then({
success: this.loadFeatures,
scope: this
}).then({
success: this.loadParentStories,
scope: this
}).then({
success: this.loadChildStories,
scope: this
}).then({
success: this.loadTasks,
failure: this.onFailure,
scope: this
}).then({
success: function(results) {
results = _.flatten(results);
_.each(results, function(result){
console.log(result.data.FormattedID, 'Estimate: ', result.data.Estimate, 'WorkProduct:', result.data.WorkProduct.FormattedID, 'TimeSpent', result.data.TimeSpent );
});
this.makeGrid(results);
},
failure: function(error) {
console.log('oh, noes!');
},
scope: this
});
},
loadFeatures: function(initiatives) {
var promises = [];
_.each(initiatives, function(initiative) {
var features = initiative.get('Children');
if(features.Count > 0) {
features.store = initiative.getCollection('Children',{fetch: ['Name','FormattedID','UserStories']});
promises.push(features.store.load());
}
});
return Deft.Promise.all(promises);
},
loadParentStories: function(features) {
features = _.flatten(features);
var promises = [];
_.each(features, function(feature) {
var stories = feature.get('UserStories');
if(stories.Count > 0) {
stories.store = feature.getCollection('UserStories', {fetch: ['Name','FormattedID','Children']});
promises.push(stories.store.load());
}
});
return Deft.Promise.all(promises);
},
loadChildStories: function(parentStories) {
parentStories = _.flatten(parentStories);
var promises = [];
_.each(parentStories, function(parentStory) {
var children = parentStory.get('Children');
if(children.Count > 0) {
children.store = parentStory.getCollection('Children', {fetch: ['Name','FormattedID','Tasks']});
promises.push(children.store.load());
}
});
return Deft.Promise.all(promises);
},
loadTasks: function(stories) {
stories = _.flatten(stories);
var promises = [];
_.each(stories, function(story) {
var tasks = story.get('Tasks');
if(tasks.Count > 0) {
tasks.store = story.getCollection('Tasks', {fetch: ['Name','FormattedID','Workproduct','Estimate','TimeSpent']});
promises.push(tasks.store.load());
}
else{
console.log('no tasks');
}
});
return Deft.Promise.all(promises);
},
makeGrid:function(tasks){
var data = [];
_.each(tasks, function(task){
data.push(task.data);
});
_.each(data, function(record){
record.Story = record.WorkProduct.FormattedID + " " + record.WorkProduct.Name;
});
this.add({
xtype: 'rallygrid',
showPagingToolbar: true,
showRowActionsColumn: true,
editable: false,
store: Ext.create('Rally.data.custom.Store', {
data: data,
groupField: 'Story'
}),
features: [{ftype:'groupingsummary'}],
columnCfgs: [
{
xtype: 'templatecolumn',text: 'ID',dataIndex: 'FormattedID',width: 100,
tpl: Ext.create('Rally.ui.renderer.template.FormattedIDTemplate'),
summaryRenderer: function() {
return "Totals";
}
},
{
text: 'Name',dataIndex: 'Name'
},
{
text: 'TimeSpent',dataIndex: 'TimeSpent',
summaryType: 'sum'
},
{
text: 'Estimate',dataIndex: 'Estimate',
summaryType: 'sum'
}
]
});
}
});
So I have a mongoose Schema that looks like this:
var Functionary = new Schema({
person: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Person'
},
dateOfAssignment: Date,
dateOfDischarge: Date,
isActive: Boolean
});
var PositionSchema = new Schema({
name: String,
description: String,
maxHeadCount: Number,
minHeadCount: Number,
currentHeadCount: Number,
currentlyHolding: [Functionary],
historical: [Functionary],
responsibleTo: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Position'
}
});
*note that the Position document can reference itself in the ResponsibleTo field.
Now, I'm trying to build a method that will search the Positions collection, populate the currentlyHolding[].person.name field and the responsibleTo.currentlyHolding[].person.name field, and also return the total number of records found (for paging purposes on the front-end).
Here's what my code looks like:
exports.search = function(req, res) {
var result = {
records: null,
count: 0,
currentPage: req.params.page,
totalPages: 0,
pageSize: 10,
execTime: 0
};
var startTime = new Date();
var populateQuery = [
{
path: 'currentlyHolding.person',
select: 'name'
}, {
path:'responsibleTo',
select:'name currentlyHolding.person'
}
];
Position.find(
{
$or: [
{ name: { $regex: '.*' + req.params.keyword + '.*', $options: 'i' } },
{ description: { $regex: '.*' + req.params.keyword + '.*', $options: 'i' } }
]
},
{},
{
skip: (result.currentPage - 1) * result.pageSize,
limit: result.pageSize,
sort: 'name'
})
.exec()
.populate(populateQuery)
.then(function(doc) {
result.records = doc;
});
Position.count(
{
$or: [
{ name: { $regex: '.*' + req.params.keyword + '.*', $options: 'i' } },
{ description: { $regex: '.*' + req.params.keyword + '.*', $options: 'i' } }
]
})
.exec()
.then(function(doc) {
result.count = doc;
result.totalPages = Math.ceil(result.count / result.pageSize);
})
.then(function() {
var endTime = new Date();
result.execTime = endTime - startTime;
return res.json(200, result);
});
};
My problem is when I run the first query with the populate method (as is shown), it doesn't work. I take away the populate and it works. Is it true that the populate method will break the promise? If so, are there better ways to achieve what I want?
It's been a while since you asked, but this might still help others so here we go:
According to the docs, .populate() doesn't return a promise. For that to happen you should chain .execPopulate().
Alternatively, you can put .populate() before .exec(). The latter will terminate the query builder interface and return a promise for you.