Я пытаюсь создать приложение электронной коммерции, используя MongoDB, Express и GraphQL (Apollo Client / Server) в бэкэнде, и у меня возникает проблема с тем, что пользователь добавляет что-то в свою корзину, которая хранится в БД.
На данный момент у меня есть три модели в моей БД: User
, Item
, CartItem
.
Проблема, с которой я столкнулся, заключается в том, что у меня есть модель пользователя, которая имеет *Поле 1008 *, представляющее собой массив CartItem.
У меня есть модель CartItem, которая имеет item
(отображает cartItem
в корзине на исходную Item
) и user
ссылки.
После запуска мутации с внешнего интерфейса, у меня на сервере есть распознаватель addtoCart
, который создает новый CartItem
, если для этого user.id
или item.id
уже не существует CartItem
, или просто обновляет количествоиз CartItem
, если он существует.
addToCart: async (_, { id }, ctx) => {
// 1. make sure they are signed in
const { userId } = ctx.req;
if (!userId) {
throw new Error('You must be signed in!');
}
// 2. query the users current cart
const existingCartItem = await CartItem.findOne({
user: userId,
item: id
});
// 3. check if that item is already in their cart and if it is increment by 1
if (existingCartItem) {;
return CartItem.findOneAndUpdate({
_id: existingCartItem.id,
}, {
$set: {
quantity: existingCartItem.quantity + 1
}
});
}
// 4. if its not, create a fresh CartItem for that user
return CartItem({ user: userId, item: id }).save();
}
Это прекрасно работало для добавления нового CartItem
и обновления количества, если оно уже существовало.Но поле cart
для User
не обновлялось с id
для CartItem
.Поэтому я добавил еще одну строку в свой преобразователь, чтобы обновить User
и вставить cartItem.id
в массив cart
.
addToCart: async (_, { id }, ctx) => {
// 1. make sure they are signed in
const { userId } = ctx.req;
if (!userId) {
throw new Error('You must be signed in!');
}
// 2. query the users current cart
const existingCartItem = await CartItem.findOne({
user: userId,
item: id
});
// 3. check if that item is already in their cart and if it is increment by 1
if (existingCartItem) {
return CartItem.findOneAndUpdate({
_id: existingCartItem.id,
}, {
$set: {
quantity: existingCartItem.quantity + 1
}
});
}
// 4. if its not, create a fresh CartItem for that user
const cartItem = await CartItem({ user: userId, item: id }).save();
//EXTRA: push cartItem id to User cart array
await User.findOneAndUpdate({
"_id": userId
}, {
$push: {
cart: cartItem.id
}
});
return cartItem
}
. Когда я делаю это, сервер начинает зависать, и любые вызовы API вызываютделает киоски и в основном ломает приложение без ошибок, которые я вижу.Если я удаляю код, который обновляет вызовы API User.cart
, они снова работают, но возвращаются к User
, который не обновляется.Я что-то упустил?
UserSchema выглядит так:
const mongoose = require('mongoose');
const { Schema } = mongoose;
mongoose.Promise = global.Promise;
const md5 = require('md5');
const validator = require('validator');
const mongodbErrorHandler = require('mongoose-mongodb-errors');
const passportLocalMongoose = require('passport-local-mongoose');
// document structure
const userSchema = new Schema({
fullName: {
type: String,
required: 'Please supply a name',
trim: true,
lowercase: true
},
username: {
type: String,
required: 'Please supply a username',
trim: true,
lowercase: true
},
email: {
type: String,
unique: true,
lowercase: true,
trim: true,
validate: [validator.isEmail, 'Invalid Email Address'],
required: 'Please supply an email address'
},
password: {
type: String,
minlength: 6
},
resetToken: String,
resetTokenExpiry: String,
cart: [{
type: mongoose.Schema.ObjectId,
ref: 'CartItem'
}],
createdAt: {
type: Date,
default: Date.now
},
updatedAt: {
type: Date,
default: Date.now
},
permissions: [String],
});
// grab gravatar image based on email addresss
userSchema.virtual('gravatar').get(function() {
const hash = md5(this.email);
return `https://gravatar.com/avatar/${hash}?s=200`;
});
// grab first name and add as virtual field
userSchema.virtual('firstName').get(function() {
return this.fullName.split(' ')[0];
});
// grab last name and add as virtual field
userSchema.virtual('lastName').get(function() {
const names = this.fullName.split(' ');
return names[names.length - 1];
});
// plugins
userSchema.plugin(passportLocalMongoose, { usernameField: 'email' });
userSchema.plugin(mongodbErrorHandler);
function autopopulate(next) {
this.populate('cart');
next();
}
userSchema.pre('find', autopopulate);
userSchema.pre('findOne', autopopulate);
// compile model and export
module.exports = mongoose.model('User', userSchema);
И моя схема CartItem выглядит так:
const mongoose = require('mongoose');
const { Schema } = mongoose;
mongoose.Promise = global.Promise;
// document structure
const cartItemSchema = new Schema({
quantity: {
type: Number,
default: 1
},
item: {
type: mongoose.Schema.ObjectId,
ref: 'Item',
require: 'You must supply a item'
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
require: 'You must supply a user'
},
createdAt: {
type: Date,
default: Date.now
},
updatedAt: {
type: Date,
default: Date.now
}
});
function autopopulate(next) {
this.populate('item');
this.populate('user');
next();
}
cartItemSchema.pre('find', autopopulate);
cartItemSchema.pre('findOne', autopopulate);
// compile model and export
module.exports = mongoose.model('CartItem', cartItemSchema);