Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ const User = sequelize.import('models/user');

// Ваши relations между моделями :)

Cart.belongsTo(User);
Review.belongsTo(User);
Souvenir.belongsTo(Country);
Souvenir.hasMany(Review);

Souvenir.belongsToMany(Tag, { through: 'souvenir_tags' });
Cart.belongsToMany(Souvenir, { through: 'cart_souvenirs' });

module.exports.sequelize = sequelize;

module.exports.Country = Country;
Expand Down
15 changes: 15 additions & 0 deletions models/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,19 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель корзины
return sequelize.define('carts', {
id: {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

если связи указаны правильно, id'шки сами подтянутся

type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
userId: {
type: DataTypes.INTEGER,
reference: {
model: 'users',
key: 'id'
}
}
});
};
19 changes: 19 additions & 0 deletions models/country.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,23 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель страны
return sequelize.define('countries', {
id: {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тоже

type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.TEXT,
unique: true
}
}, {
indexes: [
{
name: 'countries_name',
fields: ['name']
}
]
});
};
31 changes: 31 additions & 0 deletions models/review.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,35 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель отзыва
return sequelize.define('reviews', {
id: {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тоже

type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
text: DataTypes.TEXT,
rating: {
type: DataTypes.INTEGER,
validate: { min: 0, max: 5 }
},
isApproved: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
souvenirId: {
type: DataTypes.INTEGER,
reference: {
model: 'souvenirs',
key: 'id'
}
},
userId: {
type: DataTypes.INTEGER,
reference: {
model: 'users',
key: 'id'
}
}
});
};
39 changes: 39 additions & 0 deletions models/souvenir.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,43 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель сувенира
return sequelize.define('souvenirs', {
id: {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тоже

type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
image: DataTypes.TEXT,
price: {
type: DataTypes.DOUBLE,
allowNull: false,
validate: { min: 0 }
},
rating: {
type: DataTypes.DOUBLE,
validate: { min: 0, max: 5 }
},
amount: {
type: DataTypes.INTEGER,
validate: { min: 0 },
defaultValue: 0
},
isRecent: DataTypes.BOOLEAN,
countryId: {
type: DataTypes.INTEGER,
reference: {
model: 'countries',
key: 'id'
}
}
}, {
indexes: [{
fields: ['rating', 'price']
}]
});
};
9 changes: 9 additions & 0 deletions models/tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель тэга
return sequelize.define('tags', {
id: {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

тоже

type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: DataTypes.TEXT
});
};
12 changes: 12 additions & 0 deletions models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,16 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель юзера
return sequelize.define('users', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
login: {
type: DataTypes.TEXT,
unique: true
}
});
};
123 changes: 121 additions & 2 deletions queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,55 @@

class Queries {
constructor(models) {
// Что-нибудь инициализируем в конструкторе
this.sequelize = models.sequelize;
this.op = models.sequelize.Op;
this.country = models.Country;
this.tag = models.Tag;
this.review = models.Review;
this.souvenir = models.Souvenir;
this.cart = models.Cart;
this.user = models.User;
}

// Далее идут методы, которые вам необходимо реализовать:

getAllSouvenirs() {
// Данный метод должен возвращать все сувениры.
return this.souvenir.findAll();
}

getCheapSouvenirs(price) {
// Данный метод должен возвращать все сувениры, цена которых меньше или равна price.
return this.souvenir.findAll({
where: { price: { [this.op.lte]: price } }
});
}

getTopRatingSouvenirs(n) {
// Данный метод должен возвращать топ n сувениров с самым большим рейтингом.
return this.souvenir.findAll({
order: [
['rating', 'DESC']
],
limit: n
});
}

getSouvenirsByTag(tag) {
// Данный метод должен возвращать все сувениры, в тегах которых есть tag.
// Кроме того, в ответе должны быть только поля id, name, image, price и rating.
return this.souvenir.findAll({
attributes: ['id', 'name', 'image', 'price', 'rating'],
include: [
{
model: this.tag,
where: {
name: tag
},
attributes: []
}
]
});
}

getSouvenirsCount({ country, rating, price }) {
Expand All @@ -31,36 +60,126 @@ class Queries {

// Важно, чтобы метод работал очень быстро,
// поэтому учтите это при определении моделей (!).
return this.souvenir.count({
where: {
rating: { [this.op.gte]: rating },
price: { [this.op.lte]: price }
},
include: [
{
model: this.country,
where: {
name: country
}
}
]
});
}

searchSouvenirs(substring) {
// Данный метод должен возвращать все сувениры, в название которых входит
// подстрока substring. Поиск должен быть регистронезависимым.
return this.souvenir.findAll({
where: {
name: { [this.op.iLike]: `%${substring}%` }
}
});
}

getDisscusedSouvenirs(n) {
// Данный метод должен возвращать все сувениры, имеющих >= n отзывов.
// Кроме того, в ответе должны быть только поля id, name, image, price и rating.
return this.souvenir.findAll({
attributes: ['id', 'name', 'image', 'price', 'rating'],
include: [
{
model: this.review,
attributes: []
}
],
order: ['id'],
group: 'souvenirs.id',
having: this.sequelize.where(
this.sequelize.fn('COUNT', this.sequelize.col('reviews.id')), '>=', n
)
});
}

deleteOutOfStockSouvenirs() {
// Данный метод должен удалять все сувениры, которых нет в наличии
// (то есть amount = 0).

// Метод должен возвращать количество удаленных сувениров в случае успешного удаления.
return this.souvenir.destroy({
where: {
amount: 0
}
});
}

addReview(souvenirId, { login, text, rating }) {
async addReview(souvenirId, { login, text, rating }) {
// Данный метод должен добавлять отзыв к сувениру souvenirId
// содержит login, text, rating - из аргументов.
// Обратите внимание, что при добавлении отзыва рейтинг сувенира должен быть пересчитан,
// и всё это должно происходить за одну транзакцию (!).
return this.sequelize.transaction(async transaction => {
const user = await this.user.findOne({
where: {
login: login
},
transaction: transaction
});
await this.review.create(
{
text: text,
rating: rating,
souvenirId: souvenirId,
userId: user.id
},
{
transaction: transaction
}
);

const ratings = (await this.review.findAll({
where: {
souvenirId: souvenirId
},
transaction: transaction
})).map(review => review.rating);

rating = ratings.reduce((sum, current) => sum + current, 0) / ratings.length;

const souvenir = await this.souvenir.findOne({
where: {
id: souvenirId
},
transaction: transaction
});

await souvenir.update({ rating }, { transaction });
});
}

getCartSum(login) {
// Данный метод должен считать общую стоимость корзины пользователя login
// У пользователя может быть только одна корзина, поэтому это тоже можно отразить
// в модели.
return this.cart.sum('souvenirs.price', {
group: 'carts.id',
includeIgnoreAttributes: false,
include: [
{
model: this.souvenir
},
{
model: this.user,
where: {
login: login
}
}
]
});
}
}

Expand Down