ReactJS / Javascript / Prisma query - Nested aggregate not working - javascript

I'm trying to make a query which calculates the total price of the shopping basket.
A basket contains basketProducts, and a basketProduct has a product which contains all the information such as price.
I tried it as follows:
const basket = await prisma.basketProduct.aggregate({
where: {
basketId: id,
},
_sum: {
select: {
product: {
price: true,
},
},
},
});
Error
Unknown field `select` for select statement on model BasketProductSumAggregateOutputType. Available options are listed in green. Did you mean `amount`?
I also tried it as follows but it doesn't work.
const basket = await prisma.basketProduct.aggregate({
where: {
basketId: id,
},
_sum: {
product: {
price: true,
},
},
});
Error
Unknown field `product` for select statement on model BasketProductSumAggregateOutputType. Available options are listed in green. Did you mean `productId`?

Related

Is there a way to update an object in an array of a document by query in Mongoose?

I have got a data structure:
{
field: 1,
field: 3,
field: [
{ _id: xxx , subfield: 1 },
{ _id: xxx , subfield: 1 },
]
}
I need to update a certain element in the array.
So far I can only do that by pulling out old object and pushing in a new one, but it changes the file order.
My implementation:
const product = await ProductModel.findOne({ _id: productID });
const price = product.prices.find( (price: any) => price._id == id );
if(!price) {
throw {
type: 'ProductPriceError',
code: 404,
message: `Coundn't find price with provided ID: ${id}`,
success: false,
}
}
product.prices.pull({ _id: id })
product.prices.push(Object.assign(price, payload))
await product.save()
and I wonder if there is any atomic way to implement that. Because this approach doesn't seem to be secured.
Yes, you can update a particular object in the array if you can find it.
Have a look at the positional '$' operator here.
Your current implementation using mongoose will then be somewhat like this:
await ProductModel.updateOne(
{ _id: productID, 'prices._id': id },//Finding Product with the particular price
{ $set: { 'prices.$.subField': subFieldValue } },
);
Notice the '$' symbol in prices.$.subField. MongoDB is smart enough to only update the element at the index which was found by the query.

Sequelize query table by column and associated record's column

I am trying to query for music albums that are associated with an exact amount of artists.
I am correctly associating the models using a many-to-many table but my query is failing to do what I'd like it to do.
albumArtists = albumArtists.map((artist) => {
return artist.id;
});
const album = await DB.Album.findOne({
where: {
name: albumName,
},
include: [
{
model: DB.Artist,
through: {
where: {
artistId: {
[Op.and]: albumArtists
}
}
}
},
],
});
I am trying to query for an album with name set to albumName and has associated albumArtists with artistId matching all in albumArtists.
I tried setting my where clause outside of the through object but that didn't seem to work either.
Try to update through to this one:
through: { model: DB.AlbumArtists // write correct model name }
And also yo can simplify where condition:
where: { artistId: albumArtists }

How can I use transactions to check if an item exists in one table before updating a second table using AWS.DynamoDB.DocumentClient?

I am new to DynamoDB. I have a DynamoDB table called 'kids-profiles' that lists child profiles for a user (primary partition key 'userId', primary sort key 'childId'). I have a second table called 'kids-tasks' that lists tasks for a child (primary partition key 'childId', primary sort key 'taskId'). I would like to atomically add an item to the 'kids-tasks' table only if the 'childId' exists in the 'kids-profiles' table.
I am trying to use the transactWrite method of the AWS.DynamoDB.DocumentClient class (https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html) to achieve this, but I can't get it to work. First, when I try to add a ConditionCheck to the TransactionItems, it says "TransactItems can only contain one of Check, Put, Update or Delete". So I have changed "ConditionCheck" to "Check" instead, assuming that is correct even though it is not mentioned in the documentation. Second, when I do get it to execute, it adds the task to the 'kids-tasks' table regardless of whether or not the 'childId' exists in the 'kids-profiles' table.
const params = {
TransactItems: [
{
Check: {
TableName: "kids-profiles",
Key: {
userId: userId,
childId: childId,
},
ConditionExpression: "attribute_exists(childId)",
},
Put: {
TableName: "kids-tasks",
Item: {
childId: childId,
taskId: uuid.v1(),
title: title,
}
}
}
]
};
try
{
await dynamoDb.transactWrite(params).promise();
console.log("Success!");
}
catch (error)
{
console.log("Error");
}
Divide objects into individual Operations.
const params = {
TransactItems: [
{
ConditionCheck: {
...
},
+ }, // <------ !!!!!
+ {
Put: {
...
}
]
};

How to get attributes from through table in query?

I'm making a simple shopping cart. I have a Cart model, a Product model and a through table CartItems. These are the associations:
models.Cart.belongsToMany(models.Product, { through: 'CartItems', as: 'items' })
models.Product.belongsToMany(models.Cart, { through: "CartItems" });
These are the definitions of the models:
Cart Model
var Cart = sequelize.define('Cart', {
userId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'User',
key: 'id',
},
},
totalPrice: DataTypes.FLOAT
});
Product Model
var Product = sequelize.define('Product', {
code: DataTypes.STRING,
name: DataTypes.STRING,
price: DataTypes.FLOAT
});
CartItems Model
In this model I added a quantity and price attribute because I read somewhere that it's good to have a history of what the price was when they made the order. And the quantity attribute because I just want to change the quantity if another product is added instead of adding another row.
var CartItem = sequelize.define('CartItem', {
CartId: DataTypes.INTEGER,
ProductId: DataTypes.INTEGER,
quantity: DataTypes.INTEGER,
price: DataTypes.FLOAT
});
I know this might not be the best way to do things but even if I change the implementation I would like to know: how do I access an attribute that's in the through table?
Specifically I'm trying to do the following for the checkout function:
Cart.prototype.checkout = async function () {
let cartItemArray = await this.getItems({
include: [{
model: Product,
through: {
attributes: ['quantity'],
}
}]
});
cartItemArray = cartItemArray.map((item) => {
return {
code: item.code,
price: item.price,
quantity: item.quantity
};
});
let total = getTotalPrice(cartItemArray);
return total;
};
First, a few warnings
Warning 1. You have a price field both in your Product model and in your CartItem model. Are you sure you want this? In your attempt to write that checkout() method, when you do item.price, which of those prices did you want to get? My intuition tells me you didn't really want to have two fields, but if you really do, consider renaming one of them to avoid ambiguity.
Warning 2. You have a totalPrice in your Cart model... Is this field supposed to keep track of the sum of the prices of the associated products? If yes, that is a bad idea, remove that field altogether and whenever you need the sum, compute it at that very moment, because keeping duplicate data like this is very error prone (you must ensure they are in sync).
Mistake 1
You explicitly defined the junction table model, i.e. CartItem, with the following code:
var CartItem = sequelize.define('CartItem', { /* ... */ });
So far so good. But when you define the many-to-many relationship, you made a mistake. You used through: "CartItems" but you should have used through: "CartItem". Actually, the best practice in this case is to refer directly to the model, since you have it: through: CartItem. Because of this Sequelize ended up ignoring your model and creating a junction table automatically without your extra fields price and quantity.
Mistake 2
In your attempt to write the checkout() method you did:
this.getItems({
include: [{
model: Product,
through: {
attributes: ['quantity'],
}
}]
});
This does not make sense. Recall that Item is just an alias you set up for Product. Running this code yields SequelizeEagerLoadingError: Product is not associated to Product!.
Instead, you can simply perform a this.getItems() without any parameters at all.
Mistake 3
Next, you wrote the code:
return {
code: item.code,
price: item.price,
quantity: item.quantity
};
which suggests that you were expecting that quantity came as another field alongside code. This is incorrect. code is a field from the Product model while quantity is a field from the CartItem model. Sequelize will not retrieve them "flattened" like this. Instead, the fields from the association itself come nested in the query result, like this:
{
"id": 1,
"code": null,
"name": "test",
"price": null,
"createdAt": "2018-03-11T19:11:12.862Z",
"updatedAt": "2018-03-11T19:11:12.862Z",
"CartItem": {
"CartId": 1,
"ProductId": 1,
"quantity": 2,
"price": 1.5,
"createdAt": "2018-03-11T19:11:13.047Z",
"updatedAt": "2018-03-11T19:11:13.047Z"
}
}
Therefore, instead of item.quantity there, you should use item.CartItem.quantity.
Summarizing
The answer to the question in title, "How to get attributes from through table in query?" is simply "just do the query, i.e., this.getItems() in your case, since the attributes from through table come in the result by default".
It's just that you made a few other mistakes and of course, it didn't work.

Meteor Calculated Value in Collection Field

Is there a way to have a allways calculated value in Meteor collection field? I am currently developing an app to manage inventory of sandwiches. Each sandwich can depend on ingredients in other collections. I need to have a field always auto calculated to the number of the ingredient that is lowest in stock. How can i achieve this? I can not find anything about this when I Google, is it possible that Meteor does not have any support for this?
This sounds like a job for a collection hook. Collection hooks allow you to execute an action before/after collections are inserted/updated/etc.
Let's say you have an ingredients collection. Perhaps that ingredients collection has a schema like:
Ingredients = new Mongo.Collection('ingredients');
IngredientsSchema = new SimpleSchema({
"name": {
type: String
},
"quantity": {
type: Number
}
});
Ingredients.attachSchema(IngredientsSchema);
Then you have a sandwiches collection with a hypothetical schema:
Sandwiches = new Mongo.Collection('sandwiches');
SandwichesSchema = new SimpleSchema({
"name": {
type: String
},
"ingredients": {
type: [String],
label: "An array of ingredient internal ids (_id)"
},
"quantity": {
type: Number
}
});
Sandwiches.attachSchema(SandwichesSchema);
Your collection hook would be something along the lines of:
Ingredients.after.update(function(userId, doc, fieldNames, modifier, options) {
// Find the ingredient with the lowest value
ingredient = Ingredients.findOne({}, { sort: { quantity: 1 } });
if(ingredient && ingredient._id == doc._id) {
//If the ingredient matches this ingredient, update all sandwiches who have the agreement to reflect the remaining quantity of ingredients.
Sandwiches.update({ ingredients: doc._id }, { $set: { quantity: doc.quantity } }, { multi: true });
}
});
You'll probably also need a collection hook after inserting an ingredient, but this should be plenty to get you started.

Categories

Resources