Skip to content
Open
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
80 changes: 55 additions & 25 deletions queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,22 @@
module.exports = class Queries {
constructor(mongoose, { souvenirsCollection, cartsCollection }) {
const souvenirSchema = mongoose.Schema({ // eslint-disable-line new-cap
// Ваша схема сувенира тут
_id: mongoose.Schema.Types.ObjectId,
tags: [String],
reviews: [mongoose.Schema.Types.Mixed],
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Всё таки для большей надёжности лучше определить схему Review, а в этой строке сделать reviews: [reviewSchema]

name: String,
image: String,
price: { type: Number, index: true },
amount: Number,
country: { type: String, index: true },
rating: { type: Number, index: true },
isRecent: Boolean
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Было бы круто явным образом указать default и required для полей


const cartSchema = mongoose.Schema({ // eslint-disable-line new-cap
// Ваша схема корзины тут
_id: mongoose.Schema.Types.ObjectId,
login: String,
items: [{ souvenirId: String, amount: Number }]
});

// Модели в таком формате нужны для корректного запуска тестов
Expand All @@ -18,42 +29,38 @@ module.exports = class Queries {
// Далее идут методы, которые вам необходимо реализовать:

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

getCheapSouvenirs(price) {
// Данный метод должен возвращать все сувениры, цена которых меньше или равна price
return this._Souvenir.find({ price: { $lte: price } });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Можно немного симпатичнее this._Souvenir.where('price').lte(price), но это не замечание)

}

getTopRatingSouvenirs(n) {
// Данный метод должен возвращать топ n сувениров с самым большим рейтингом
return this._Souvenir.find()
.sort({ rating: -1 })
.limit(n);
}

getSouvenirsByTag(tag) {
// Данный метод должен возвращать все сувениры, в тегах которых есть tag
// Кроме того, в ответе должны быть только поля name, image и price
return this._Souvenir.find({ tags: tag }, { name: 1, image: 1, price: 1, _id: 0 });
}

getSouvenrisCount({ country, rating, price }) {
// Данный метод должен возвращать количество сувениров,
// из страны country, с рейтингом больше или равной rating,
// и ценой меньше или равной price

// ! Важно, чтобы метод работал очень быстро,
// поэтому учтите это при определении схем
return this._Souvenir.find({ country, rating: { $gte: rating }, price: { $lte: price } })
.count();
Copy link
Copy Markdown

@evgenymarkov evgenymarkov Apr 18, 2018

Choose a reason for hiding this comment

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

Давай немного приберёмся здесь с форматированием, выглядит странно. Кстати, можно обойтись без find и сразу написать эти же параметры в count.

}

searchSouvenirs(substring) {
// Данный метод должен возвращать все сувениры, в название которых входит
// подстрока substring. Поиск должен быть регистронезависимым
return this._Souvenir.find({ name: { $regex: substring, $options: 'i' } });
}

getDisscusedSouvenirs(date) {
// Данный метод должен возвращать все сувениры,
// первый отзыв на которые был оставлен не раньше даты date
return this._Souvenir.find({ 'reviews.0.date': { $gte: date } });
}

deleteOutOfStockSouvenirs() {
return this._Souvenir.find({ amount: { $eq: 0 } }).remove();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this._Souvenir.deleteMany({ amount: 0 }); несколько лаконичнее

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

Expand All @@ -62,16 +69,39 @@ module.exports = class Queries {
}

async addReview(souvenirId, { login, rating, text }) {
// Данный метод должен добавлять отзыв к сувениру souvenirId, отзыв добавляется
// в конец массива (чтобы сохранить упорядоченность по дате),
// содержит login, rating, text - из аргументов,
// date - текущая дата и isApproved - false
// Обратите внимание, что при добавлении отзыва рейтинг сувенира должен быть пересчитан
let reviewsCount = 1;
let ratingSum = rating;
await this._Souvenir.find({ _id: souvenirId })
.then(souvenir => souvenir[0].reviews
.forEach(review => {
reviewsCount++;
ratingSum += review.rating;
})
);

let review = {
login,
rating,
text,
date: new Date(),
isApproved: false
};
await this._Souvenir.findOneAndUpdate({ _id: souvenirId }, {
$push: { reviews: review },
$set: { rating: ratingSum / reviewsCount }
});
// await
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

?

}

async getCartSum(login) {
// Данный метод должен считать общую стоимость корзины пользователя login
// У пользователя может быть только одна корзина, поэтому это тоже можно отразить
// в схеме
const cart = ((await this._Cart.find({ login }, { items: 1, _id: 0 }))[0]).items;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

findOne


let sum = 0;
for (let orderedSouvenir of cart) {
const souvenir = (await this._Souvenir.find({ _id: orderedSouvenir.souvenirId }))[0];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Await в цикле это верный сигнал к тому, что стоит переписать кусок кода.
В большинстве случаев хочется получить N ресурсов и сделать что-то с ними независимо от порядка, тогда адекватнее применить Promise.all().
Но здесь есть второй нюанс – N обращений к БД в цикле. Этого можно и нужно избегать потому что всякая ODM/ORM да и голый SQL позволяют тебе сделать чуть более сложную выборку, но загрузить все нужные данные за 1 запрос.

sum += souvenir.price * orderedSouvenir.amount;
}

return sum;
}
};