Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Дубровин Алексей #16

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
9 changes: 9 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ const Cart = sequelize.import('models/cart');
const User = sequelize.import('models/user');

// Ваши relations между моделями :)
Cart.belongsTo(User);
Cart.belongsToMany(Souvenir, { through: 'cart_souvenirs' });
Review.belongsTo(Souvenir);

Choose a reason for hiding this comment

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

Souvenir.hasMany(Review) достаточно

Review.belongsTo(User);
User.hasOne(Cart);

Choose a reason for hiding this comment

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

Уже есть Cart.belongsTo(User)

User.hasMany(Review);

Choose a reason for hiding this comment

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

Также с Review.belongsTo(User)

Souvenir.hasMany(Review);
Souvenir.belongsTo(Country);
Souvenir.belongsToMany(Tag, { through: 'souvenir_tags' });

module.exports.sequelize = sequelize;

Expand Down
15 changes: 14 additions & 1 deletion models/cart.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
'use strict';

module.exports = (sequelize, DataTypes) => {
// Ваша модель корзины
return sequelize.define('cart', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
userId: {
type: DataTypes.INTEGER,
allowNull: true
}
}, {
timestamps: true
});

Choose a reason for hiding this comment

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

Нет необходимости явным образом определять что либо в этой модели.
id, createdAt, updatedAt проставляются неявно Sequelize'ом.
userId также появится засчёт связи Cart.belongsTo(User).

};
15 changes: 14 additions & 1 deletion models/country.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
'use strict';

module.exports = (sequelize, DataTypes) => {
// Ваша модель страны
return sequelize.define('country', {
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
},
name: {
type: DataTypes.TEXT,
allowNull: true
}
}, {
timestamps: true
});
};
32 changes: 31 additions & 1 deletion models/review.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
'use strict';

module.exports = (sequelize, DataTypes) => {
// Ваша модель отзыва
return sequelize.define('review', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
text: {
type: DataTypes.TEXT,
allowNull: true
},
rating: {
type: DataTypes.INTEGER,
allowNull: true
},
isApproved: {
type: DataTypes.BOOLEAN,
allowNull: true,
default: false
},
souvenirId: {
type: DataTypes.INTEGER,
allowNull: true
},
userId: {
type: DataTypes.INTEGER,
allowNull: true
}
}, {

Choose a reason for hiding this comment

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

Аналогично про id и другие *Idы

timestamps: true
});
};
45 changes: 44 additions & 1 deletion models/souvenir.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
'use strict';

module.exports = (sequelize, DataTypes) => {
// Ваша модель сувенира
return sequelize.define('souvenir', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
image: {
type: DataTypes.TEXT,
allowNull: true
},
price: {
type: DataTypes.DOUBLE,
allowNull: false
},
rating: {
type: DataTypes.DOUBLE,
allowNull: true
},
amount: {
type: DataTypes.INTEGER,
allowNull: true,
default: 0
},
isRecent: {
type: DataTypes.BOOLEAN,
allowNull: true
},
countryId: {
type: DataTypes.INTEGER,
allowNull: true
}
}, {

Choose a reason for hiding this comment

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

Всё также, много лишних явных объявлений

timestamps: true,
indexes: [
{
fields: ['countryId', 'rating', 'price']
}
]
});
};
15 changes: 14 additions & 1 deletion models/tag.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
'use strict';

module.exports = (sequelize, DataTypes) => {
// Ваша модель тэга
return sequelize.define('tag', {
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: true
}
}, {
timestamps: true
});
};
15 changes: 14 additions & 1 deletion models/user.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
'use strict';

module.exports = (sequelize, DataTypes) => {
// Ваша модель юзера
return sequelize.define('user', {
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
},
login: {
type: DataTypes.TEXT,
allowNull: true
}
}, {
timestamps: true
});
};
2 changes: 1 addition & 1 deletion playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const Queries = require('./queries');

try {
// Здесь можно делать запросы, чтобы проверять, что они правильно работают
const result = await queries.getAllSouvenirs();
const result = await queries.getDisscusedSouvenirs(5);

console.info(result);
} catch (error) {
Expand Down
106 changes: 77 additions & 29 deletions queries.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,114 @@
'use strict';
const Sequelize = require('sequelize');

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

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

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

getCheapSouvenirs(price) {
// Данный метод должен возвращать все сувениры, цена которых меньше или равна price.
return this.Souvenir.findAll({
where: {
price: { [Sequelize.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 }) {
// Данный метод должен возвращать количество сувениров,
// из страны country, с рейтингом больше или равной rating,
// и ценой меньше или равной price.

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

searchSouvenirs(substring) {
// Данный метод должен возвращать все сувениры, в название которых входит
// подстрока substring. Поиск должен быть регистронезависимым.
return this.Souvenir.findAll({
where: {
name: { [Sequelize.Op.contains]: substring }

Choose a reason for hiding this comment

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

ilike или iRegexp для регистронезависимого поиска

}
});
}

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

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

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

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

const souvenir = await this.Souvenir.findOne({ where: { souvenirId } });
const ratings = await this.Review.findAll({ where: { souvenirId } }).then(r => r.rating);
souvenir.rating = ratings.reduce((a, b) => a + b, 0) / ratings.length;

await souvenir.save();
}

Choose a reason for hiding this comment

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

Оберни в транзакцию


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

Expand Down