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

Имаев Альберт #20

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ const Cart = sequelize.import('models/cart');
const User = sequelize.import('models/user');

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

module.exports.sequelize = sequelize;

Expand Down
14 changes: 14 additions & 0 deletions models/cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,18 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель корзины
return sequelize.define('carts', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
userId: {
type: DataTypes.INTEGER,
references: {
model: 'users',
key: 'id'
}
}
});
};
10 changes: 10 additions & 0 deletions models/country.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,14 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель страны
return sequelize.define('countries', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.TEXT
}
});
};
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: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
isApproved: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
rating: {
type: DataTypes.DOUBLE
},
text: {
type: DataTypes.TEXT
},
souvenirId: {
type: DataTypes.INTEGER,
references: {
model: 'souvenirs',
key: 'id'
}
},
userId: {
type: DataTypes.INTEGER,
references: {
model: 'users',
key: 'id'
}
}
});
};
34 changes: 34 additions & 0 deletions models/souvenir.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,38 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель сувенира
return sequelize.define('souvenirs', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
amount: {
type: DataTypes.INTEGER
},
image: {
type: DataTypes.TEXT
},
isRecent: {
type: DataTypes.BOOLEAN
},
name: {
type: DataTypes.TEXT
},
price: {
type: DataTypes.DOUBLE
},
rating: {
type: DataTypes.DOUBLE
},
countryId: {
type: DataTypes.INTEGER,
references: {
model: 'countries',
key: 'id'
}
}
}, {
indexes: [{ fields: ['rating', 'price'] }]
});
};
10 changes: 10 additions & 0 deletions models/tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,14 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель тэга
return sequelize.define('tags', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
Copy link

Choose a reason for hiding this comment

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

кажется, что тут нужен индекс (в коде есть выборка по тэгу)

type: DataTypes.TEXT
}
});
};
10 changes: 10 additions & 0 deletions models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,14 @@

module.exports = (sequelize, DataTypes) => {
// Ваша модель юзера
return sequelize.define('users', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
login: {
type: DataTypes.TEXT
Copy link

Choose a reason for hiding this comment

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

и тут индекс, уникальный (не может быть двух юзеров с одинаковым логином)

}
});
};
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"main": "index.js",
"scripts": {
"lint": "eslint .",
"test": "npm run lint"
"test": "npm run lint",
"ss": "C:\\PROGRA~1\\PostgreSQL\\10\\bin\\psql.exe -h localhost -p 5432 --username postgres -f dump/dump.backup",
"start": "node playground.js"
},
"dependencies": {
"dotenv": "5.0.1",
Expand Down
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.getCheapSouvenirs(839.5);

console.info(result);
} catch (error) {
Expand Down
86 changes: 84 additions & 2 deletions queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,51 @@
class Queries {
constructor(models) {
// Что-нибудь инициализируем в конструкторе
this.sequelize = models.sequelize;
this.Op = models.sequelize.Op;
this.Cart = models.Cart;
this.Country = models.Country;
this.Review = models.Review;
this.Souvenir = models.Souvenir;
this.Tag = models.Tag;
this.User = models.User;
}

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

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

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

Choose a reason for hiding this comment

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

цена должна мыть меньше или равна

}
});
}

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 },
Copy link

Choose a reason for hiding this comment

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

сравнение лучше делать регистронезависимым

attributes: []
}
});
}

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

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

Choose a reason for hiding this comment

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

и тут регистронезависимый

attributes: []
}]
});
}

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: ['name', 'image', 'price', 'rating'],
group: 'souvenirs.id',
having: this.sequelize.where(
this.sequelize.fn('COUNT', this.sequelize.col('reviews.id')), '>=', n
),
include: {
model: this.Review,
attributes: []
},
order: ['id']
});
}

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 - из аргументов.
// Обратите внимание, что при добавлении отзыва рейтинг сувенира должен быть пересчитан,
// и всё это должно происходить за одну транзакцию (!).
const user = await this.User.findOne({ where: { login } });
const souvenir = await this.Souvenir.findById(souvenirId);

return this.sequelize.transaction(async transaction => {
const review = { userId: user.id, text, rating };
await souvenir.createReview(review, { transaction });

const reviews = await souvenir.getReviews({ transaction });
rating = (souvenir.rating * (reviews.length - 1) + rating) / (reviews.length);
Copy link

Choose a reason for hiding this comment

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

не обязательно к исправлению

сейчас ты загружаешь в приложение весь список отзывов и обновляешь рейтинг
Такие вычисления лучше делать на стороне БД


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

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

Choose a reason for hiding this comment

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

тут тоже лучше регистронезависимое сравнение

}
]
});
}
}

Expand Down