I want tree structure in my Categories table.
So I tried this:
Categories model looks like:
import * as Sequelize from 'sequelize';
export default class CategoriesModel extends Sequelize.Model {
static init(sequelize, DataTypes) {
return super.init({
name: {
type: DataTypes.STRING,
},
}, {
modelName: 'categories',
sequelize,
});
}
}
association in my CategoriesModel class looks like:
static associate({ Categories }) {
this.nestedCategories = this.hasMany(Categories, {
as: 'nestedCategories',
foreignKey: 'parentId',
});
}
and when i tried to insert rows, for example:
sequelize.sync({ force: true }).then(() => {
models.Categories.create({
name: 'parent',
nestedCategories: [
{
name: 'child 1',
},
{
name: 'child 2',
nestedCategories: [
{
name: 'child 3',
}
],
},
],
}, {
include: [models.Categories.nestedCategories]
}).then(cat => {
console.log(cat.toJSON());
})
});
result is:
{
id: 1,
name: 'parent',
nestedCategories:
[ { id: 2,
name: 'child 1',
parentId: 1,
updatedAt: 2019-02-05T08:39:45.655Z,
createdAt: 2019-02-05T08:39:45.655Z },
{ id: 3,
name: 'child 2',
parentId: 1,
updatedAt: 2019-02-05T08:39:45.657Z,
createdAt: 2019-02-05T08:39:45.657Z } ],
updatedAt: 2019-02-05T08:39:45.624Z,
createdAt: 2019-02-05T08:39:45.624Z,
parentId: null
}
child 3 does not inserted in a table.
I dont understand what i'm doing wrong...
You need to specify one include per nested category in order to make your request parsed correctly.
Here I made a function to do this recursively based on model you want to create:
function buildIncludeRecursive(model, includeTemplate) {
const include = Object.assign({}, includeTemplate);
let currInclude = include;
let currModel = model;
while(currModel[includeTemplate.as]){
currInclude.include = [Object.assign({}, includeTemplate)];
currInclude = currInclude.includes[0];
currModel = currModel[includeTemplate.as];
}
return include;
}
const model = {
name: 'parent',
nestedCategories: [
{ name: 'child 1' },
{
name: 'child 2',
nestedCategories: [
{ name: 'child 3' }
]
},
],
};
const includeTemplate = {
model: models.Categories,
as: 'nestedCategories'
};
model.categories.create(model, {
include: buildIncludeRecursive(model, includeTemplate)
});
Here's what your include is gonna be in this case:
{
model: models.Categories,
as: 'nestedCategories',
include: [
{
model: models.Categories,
as: 'nestedCategories'
}
]
}
Related
I use OneToMany relations for adding messages under conversations. Here, I want to query conversation with last messages only.
Here is an example-
const conversation = await this.conversationRepository.find({
where: [
{ user: { id: reqUser.id }, hide_from_user: false },
{ participant: { id: reqUser.id }, hide_from_participant: false }
],
order: {
updated_at: "DESC"
},
relations: {
user: true,
participant: true,
message: true
}
});
It returns all messages under particular conversation-
But I need only last message. Then how I have to query it? Please help me, here?
TypeOrm doesn't support that directly, to do that you have to make another query
import { InjectRepository } from "typeorm";
const conversation = await this.conversationRepository.find({
where: [
{ user: { id: reqUser.id }, hide_from_user: false },
{ participant: { id: reqUser.id }, hide_from_participant: false }
],
order: {
updated_at: "DESC"
},
relations: {
user: true,
participant: true,
}
});
conversation.messages = await InjectRepository(Message)
.createQueryBuilder('message')
.where('message.conversation = :id' , {conversation.id});
.limit(1)
.orderBy('message.id', 'Desc')
.getMany()
I have many to many association like this following model:
const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
const ActorMovies = sequelize.define('ActorMovies', {
MovieId: {
type: DataTypes.INTEGER,
references: {
model: Movie,
key: 'id'
}
},
ActorId: {
type: DataTypes.INTEGER,
references: {
model: Actor,
key: 'id'
}
}
});
Movie.belongsToMany(Actor, { through: ActorMovies });
Actor.belongsToMany(Movie, { through: ActorMovies });
And I succsessfully create Movie when create an Actor record with this following code:
Actor.create({
name: 'Jhony',
movies: [
{ name: 'Movie 1'}, // it will generate Movie with ID 1
{ name: 'Movie 2'} // it will generate Movie with ID 2
]
}, {
include: [ Movie ]
})
but my question how can I attach multiple existing Movie record when creating an Actor?
I already try:
Actor.create({
name: 'Edward',
movieIds: [1, 2]
}, {
include: [ Movie ]
})
and:
Actor.create({
name: 'Edward',
movies: [{id: 1}, {id: 2}]
}, {
include: [ Movie ]
})
But stil didn't work. Anyone can help me, please. Thanks in advance
You can't link existing movies to a new actor while creating it. You need to call setMovies of the new actor model instance:
const actor = await Actor.create({
name: 'Edward',
})
await actor.setMovies([1, 2])
Also, please pay attention that if you execute more than one query that changes something in DB it would be much more reliable to use transactions to turn all this queries into one atomic operation.
I want to print parent name in excel
this is my code
function getReportT0Print(req, res) {
return new Promise((resolve, reject) => {
Product.findAll({
where: {
$and: [
{
public: true,
visible: true,
ancestry: {
$not: null,
},
},
],
},
include: [
{
model: ormDb.Document,
required: false,
},
],
attributes: ["name", "slug", "folder_path"],
})
.then(function (data) {
// console.log("data" + data.length);
var rows = [];
rows.push(["Product Name", "Slug", "File Path", "Product Parent Name"]);
data.map(function (product) {
rows.push([
product.name,
product.slug,
product.folder_path,
(here i need to print parent name)
]);
});
var workbook = new Excel.Workbook();
var sheet = workbook.addWorksheet("products_with_tags");
sheet.addRows(rows);
resolve(workbook);
return res.send("successfull");
})
.catch(function (err) {
reject(err);
});
});
}
i can print name, slug,folder_path but i don't know how to print parent name in excel file
as parent name is not present but i have given parent_id in place of parent name and want to print parent name
my SQl table look like this
("id" "name" "version" "published" "reviewed" "visible" "public", "parent_id")
You need to register an association between two Product models like this:
Product.belongsTo(Product, { foreignKey: 'parent_id', as: 'parent' });
You can place it in some module outside model's module file where you create Sequelize instance, for example.
To use this association you need to use the same include option as you did with Document:
Product.findAll({
where: {
$and: [
{
public: true,
visible: true,
ancestry: {
$not: null,
},
},
],
},
include: [
{
model: Product,
as: 'parent',
required: false, // or true if parent_id is required field
// or you wish to get all products with parents
attributes: ["name", "slug", "folder_path"],
},
{
model: ormDb.Document,
required: false,
},
],
attributes: ["name", "slug", "folder_path"],
})
I have two models which are Licences and Items.
models.Licences.hasMany(models.Items, {
foreignKey: 'licenceId',
onDelete: 'CASCADE',
});
Licence can has many Items.
Items model has licenceId and description.
try {
return await db.Licences.findAll({
order: [['createdAt', 'DESC']],
attributes: [
'id',
'desc',
'version',
[db.Sequelize.col('Items.description'), 'itemDesc']
],
include: [
{
model: db.Items,
attributes: []
},
],
});
} catch (error) {
throw error;
}
}
This method return only one item description. However I want to return response as;
[
{
id: 1,
desc: 'LicenceOne',
itemDesc: [
"exone",
"extwo"
]
}
]
How can I do this?
You can try the below way and make changes.
try {
return await db.Licences.findAll({
order: [['createdAt', 'DESC']],
attributes: [
'id',
'desc',
'version',
],
include: [
{
model: db.Items,
attributes: ['description'],
},
],
});
} catch (error) {
throw error;
}
}
But you will get the below output. Not exactly how you want but you can reprocess all array object and have all description as a array of string in each object. Sequelize will not automatically process the array directly as per what I know.
[
{
id: 1,
desc: 'LicenceOne'
version: '1.1.1',
Items: [
{
description: 'the desctiption 1'
},
{
description: 'the desctiption 2'
},
{
description: 'the desctiption 3'
},
]
},
{
id: 2,
// and on...
}
]
My problem:
I am creating an route which will return some informations about a group, it has an id, an user assigned and also has some documents. I just want to show how much documents exists, in SQL would be SELECT COUNT, but how can i do this in this in sequelize?
My code:
async list(req, res){
const docGroups = await DocGroup.findAll({
raw: true,
include: [{
model: User,
as: 'userAssigned'
},
{
model: Document,
as: 'Document'
}
]
}).then(groups => {
const result = groups.map(group => {
return Object.assign(
{},
{
id: group.id,
name: group.name,
userAssinged: group['userAssigned.firstName'],
docAmount: // I want to put documents' count here
}
)
})
console.log(groups)
})
}
What is printed in console.log(groups):
[
{
id: 1,
name: 'pleaseworks',
createdAt: 2020-06-10T02:38:11.531Z,
updatedAt: 2020-06-10T02:38:11.531Z,
'userAssigned.id': 1,
'userAssigned.firstName': 'Please',
'userAssigned.lastName': 'Works',
'userAssigned.email': 'pleaseworks#gmail.com',
'userAssigned.password': '$2a$08$3BA4I4dsaQ3lsHy342344b5P41v5eHWjwqv6dve28nSdqbGvhsdS',
'userAssigned.createdAt': 2020-06-10T02:37:29.062Z,
'userAssigned.updatedAt': 2020-06-10T02:37:29.062Z,
'userAssigned.groupId': null,
'Document.id': 2,
'Document.description': 'deowkdopewkdwe',
'Document.content': 'odepodkewokodwe',
'Document.groupId': 1,
'Document.createdAt': 2020-06-10T02:43:46.005Z,
'Document.updatedAt': 2020-06-10T02:43:46.005Z
}
]
If DocGroup has many Document try something like this:
{
model: Document,
attributes: [[sequelize.fn('COUNT', sequelize.col('id')), 'docAmount']]
as: 'Document'
}