How SQL get nested relation item with limit pagination - mysql

I need get a products which under Category but with a limit of products item for paginate purpose. I am use strapi headlessCMS for my backend. i will query it from "slug" in category. And then it will return collection as below code, but i need a LIMIT for my products list.
Since Strapi is use bookshelf.js in sql. i have tried it as exmaple 2 below. I am bad in handle data, hope someone sharing idea how to do or give some solution for me. Appreciate =).
If you know in raw sql statement, please sharing with me. I'll do the search for it. The important part is given a logically idea how to do with data. So, my brain has the map can do the searching. Thanks.
Model relationship:
Category hasMany Products
Example 1
const res = await strapi.query("category").findOne({ slug });
// return
{
id: 2,
slug: 'slug-string'
....
products: [
{
id: 1,
name: 'Ice Cream',
category: 2
},
{
id: 2,
name: 'Bread',
category: 2
},
....
{
id: 100,
name: 'Paper',
category: 2
}
]
}
Example 2: i guess this is bad practice ya? Because it query all products, the better way i think is example 1.
const Product = strapi.query("product").model;
const result = await Product.query((qb) => {
qb.where("category", 2);
}).fetchPage({
pageSize: 1,
page: start, // start is a number, it will pass from front end query page=2
});

Related

NodeJS/Knex Creating Json Response

I'm currently using NodeJS with knex (Postgresql) for database stuff.
Problem:
Imagine the following two tables in database:
Table 1
PROJECT
id (pk)
name
Table 2
EMPLOYEE
id (pk)
name
project_id (fk)
I want to create a json-response to the user that looks like the following:
{
projects: [
{
id: 1,
name: 'emxample 1',
employees: [
{
id: 1,
name: 'example 1'
},
{
id: 2,
name: 'example 2'
}
]
}
]
}
and so on.
Making a query like:
let query = knex('project').select('project.*', 'employee.*').join('employee', 'employee.project_id', '=', 'project.id');
query.then((projects) => { res.json(projects); });
And using res.json() does not return an array of employees. What is the way to go to achieve that?
SQL responses are flat tables by their nature so in addition to knex you will need an external lib which can reconstruct flat information to nested objects.
Most of the ORM libraries know how to do it. For example objection.js which is built on top of knex uses .eager() to fetch nested relations. With objection.js ORM the query would look like this Project.query().where('id', 1).eager('employees')

How can I update a through-NM-attribute in sequelize?

I've read this question and it didnt' help me, so I'm asking my own:
Let's suppose I have 2 tables joined by a NM through table using Sequelize and MariaDB:
User <-- UserItem --> Item
A single User can have many Items, and a single Item can belong to many Users. But I need a custom through attribute to store the quantity of the Item, let's call it Apples. So, according to the docs, this will be the definition:
var UserItem = Sequelize.define('UserItem', {
quantity: DataTypes.INTEGER
},
timestamps: false
});
models.Item.belongsToMany(models.User, {through: 'UserItem'});
models.User.belongsToMany(models.Item, {through: 'UserItem'});
And then I add a new relationship with the through attribute like this:
User.addItem(item, { quantity: 0 });
This works as expected. But, what if I need to update the quantity of an Item? I could do the following:
User.addItem(item, { quantity: 20 });
And the quantity of my Item will be updated to 20 in case of existing, and inserted otherwise. I don't want this. I want something like that:
User.addItem(item, { quantity: quantity + 1 });
But due to the impossibility to make queries to the join tables, I am unable to get the particular NM row for updating using the previous value.
How can I achieve this? Thanks in advance.
You still have the join table DAO as specified in the docs.
In this case though you need to know if the item is new association or not.
So it will look something like.
User.hasItem( item )
.then( exists => {
if ( !exists ) {
return User.addItem( item, { quantity : 20 } )
} else {
item.UserItem.quantity += 1;
return item.UserItem.save();
}
} )
You can read more about hasAssociation in the docs as well.

Sequelize Query Group by day objects found by parent property

So, I'm using sequelize with a mysql instance and I have this hierarchy : a task has n images and also n metadata key value pairs.
I want to get all images based on userId column of task, and afterwards get them grouped by 'createdAt' column taking into consideration the day, since a normal groupby will be pointless ( no objects share the same datetime ). I did lots of test to try to group, and I ended up using this query, which gives bad results ( I have like 11 images for a task, and it retrieves 4 ). Honestly, i don't know what I'm doing wrong. Any of you have any idea?
This is the code snippet:
var userId = security.utils.getKeycloakSubject(req);
var where = {
userId: userId
};
db.image.findAll({
include: [{
model: db.task,
include: [{
model: db.metadata,
as: 'metadata'
}],
where: where
}],
group: [db.Sequelize.fn('DAY', db.Sequelize.col('image.createdAt'))]
}).then(function (images) {
return res.json(images);
}, function (error) {
return res.status(500).json(error);
})
I saw your question and also found this: Sequelize grouping by date, disregarding hours/minutes/seconds
It is a question about group the DAY(createdAt), looks similar with yours.
And my solution of GROUP BY DAY() is look like:
item.findAll({
attributes:
[[sequelize.fn('DATE_FORMAT', sequelize.col('TimeStamp'), '%H'), 'dates']],
group: [sequelize.fn('DAY', sequelize.col('TimeStamp'))]
}).
then(function(result){console.log(result)
}).
catch(function(error){}).catch(function(error) {
console.log(error);
});
So the raw SQL likes: SELECT DATE_FORMAT('TimeStamp', '%H') as 'dates' FROM tableName GROUP BY DAY('TimeStamp');
Hope it helps you, or you can show us the SQL you want to use, maybe it is easier to help you too.
Good luck.

Accessing an included object

I have the following JSON Object, which is the result of a loopback model (Classifications), with a relationship with another model (Labels).
My call to get the classifications is:
modClassification.findOne({
where: { id: classificationid },
include: 'labels' },
function( err, classification ){ ...
And this returns classification with something like
{ id: 'b01',
title: 'population',
country_id: 1,
labels:
[ { column_id: 'b1',
classification_id: 'b01',
classification_title: 'population',
dsporder: 1,
label: 'Total_Persons_Males',
country_id: 1,
id: 1 },
{ column_id: 'b2',
classification_id: 'b01',
classification_title: 'population',
dsporder: 2,
label: 'Total_Persons_Females',
country_id: 1,
id: 2 } ] }
which is what I would expect.
I now need to loop over the labels and access it's properties, but this is where I am stuck.
classification.labels[0] = undefined..
I have tried looping, each and whatever I can find online, but can't seem to get to each labels properties.
Can someone tell me what I am doing wrong/need to do?
Thanks
When you are including related models inside a findOne call, you need to JSONify the result before accessing the related records:
classification = classification.toJSON()
Then you should be able to access the included label items as you expect.
See https://docs.strongloop.com/display/public/LB/Include+filter, specifically the "Access included objects" section.
Note this does not work the same when you retrieve more than one result in an array. In that case you'll need to perform toJSON() on each item in the array.

Sequelize include (how to structure query)?

I have a query I'm trying to perform based on a one to many relationship.
As an example there is a model called Users and one called Projects.
Users hasMany Projects
Projects have many types which are stored in a type (enum) column. There are 4 different types that potentially a user may have that I want to load. The catch is I want to include the most recent project record (createdAt column) for all networks that potentially will be there. I have not found a way to structure the query for it to work as an include. I have however found a way to do a raw query which does what I want.
I am looking for a way without having to do a raw query. By doing the raw query I have to map the returned results to users I've returned from the other method, or I have to do a simple include and then trim off all the results that are not the most recent. The latter is fine, but I see this getting slower as a user will have many projects and it will keep growing steadily.
This allow serialize a json for anywhere action about a model. Read it, very well
sequelize-virtual-fields
// define models
var Person = sequelize.define('Person', { name: Sequelize.STRING });
var Task = sequelize.define('Task', {
name: Sequelize.STRING,
nameWithPerson: {
type: Sequelize.VIRTUAL,
get: function() { return this.name + ' (' + this.Person.name + ')' }
attributes: [ 'name' ],
include: [ { model: Person, attributes: [ 'name' ] } ],
order: [ ['name'], [ Person, 'name' ] ]
}
});
// define associations
Task.belongsTo(Person);
Person.hasMany(Task);
// activate virtual fields functionality
sequelize.initVirtualFields();