简体   繁体   中英

How to sum the values from multiple documents in one collection and push total sum to document in another collection

I have recipes and ingredients. Each has its own model and each has its own controller. Each recipe has multiple ingredients. In the recipe model, the ingredients are listed as an array of ingredient ids (in the code below, I call them ingIds in the recipe model). Each ingredient can belong to more than one recipe. Each ingredient has a calorie amount. In the ingredient model, I named the field calories. I'm trying to programmatically total the calorie amount for each recipe based on the combined calorie amounts of all the ingredients in the recipe. I have the code below that seems to work, kind of. I get the correct result in Postman when I get all recipes. However, I can't get the data in mongodb to update the total calorie count in the recipe document (the calorie-count field I named "score). Any ideas what I'm doing wrong?

I've tried populating the recipe model with the ingredient documents and then summing the nested calorie field, but I can't figure out how to sum the totals of the nested calorie field. I've also successfully calculated the totals and posted it to the database when creating a new ingredient using embedded url during posting, but that doesn't work because that's good only if the ingredient belongs to only one recipe. Here, I have ingredients belonging to multiple recipes.

Below are code snippets for how I'm currently approaching this. Recipe model has an array of ingredient Ids. The recipe controller looks at the ingredients documents for ones with the matching Id. Then, from the matching ingredients documents, the recipe controller sums the calories fields and saves it to "score". When I get all recipes in postman, the output in postman is correct. It shows the score field with the correct value. However, the mongodb database does not update the score field in the recipe document.

// Recipe Controller

...

exports.getPostTotals = catchAsync(async (req, res, next) => {
const stats = await Post.aggregate([
    {
      $lookup: {
        from: "ingredients", // name of the foreign collection
        localField: "ingIds",
        foreignField: "_id",
        as: "lookup-data"
      }
    },
    {
      $addFields: {
        score: {
          $sum: "$lookup-data.calories"
        }
      }
    },
    { $project: { "lookup-data": 0 } }
  ]);
  res.status(200).json({
    status: "success",
    data: {
      stats
    }
  });
});

...

// Recipe Model //

const mongoose = require("mongoose");
const slugify = require("slugify");

const postSchema = new mongoose.Schema(
  {
    ...

    ingIds: [{ type: mongoose.Schema.ObjectId, ref: "Ingredient" }],
    score: Number,
    ...

  },
  {
    toJSON: { virtuals: true },
    toObject: { virtuals: true }
  }
);

...

const Post = mongoose.model("Post", postSchema);

module.exports = Post;

// Ingredient Model //

const mongoose = require("mongoose");
const Post = require("./postModel");

const ingredientSchema = new mongoose.Schema(
  {
    ...

    calories: {
      type: Number,
      required: [true, "An ingredient must have a calorie count."]
    ...

  },
  {
    toJSON: { virtuals: true },
    toObject: { virtuals: true }
  }
);

const Ingredient = mongoose.model("Ingredient", ingredientSchema);

module.exports = Ingredient;

By the way, where are you writing the score value back to MongoDB? Here you are using aggregation - that means the $addFields - just adds the new fields to the documents from the previous stage of aggregation, so that the next stage in the aggregation finds those fields, that means the output of aggregation contains those fields. But it doesn't store that in the DB.

If you want to write the results of aggregation back to DB use $out or $merge

See https://docs.mongodb.com/manual/reference/operator/aggregation/out/

https://docs.mongodb.com/manual/reference/operator/aggregation/merge/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM