4

Hello I am creating a project to show user changes in the PopulationCount data of all the coins according to their categories.

I created a NodeJS script to scrape the data from a coin website, and I have saved it in my MongoDB cloud database as shown below.

MongoDB

In the Front-End, I have created a React App as shown: When user clicks on a coin category, it should display all the coins from today enter image description here

Here is the code that relates to showing all the Coin's Categories in my route:

 router.get(
    "/categories",
   asyncHandler(async (req, res) => {
    const categories = await Coin.find().distinct(
      "category",
      (err, results) => {
        res.json(results);
      }
    );
  })
);

I am having the biggest problem/trouble with my life because I need to add new property to this document that calculates the difference of the PopulationCount from Today's Date and Yesterday's Date and then adding this new "Trend" property to the document.

So that I can display the Coin data from Today's date and the calculated "trend" property that determines if it there was a decrease or increase in value.

How can I achieve this? So far I have written this code, but I do not know where to go from here.

router.get(
  "/categories/:category",
  asyncHandler(async (req, res) => {
    const { category } = req.params;
    const fullName = category.replace(/-/g, " ");

    // todays coins
    const today = new Date(Date.now());
    today.setHours(0, 0, 0, 0);
    const todaysCoins = await Coin.find({
      category: {
        $regex: new RegExp(fullName, "i"),
      },
      createdAt: {
        $gte: today,
      },
    }).lean();
    res.json(todaysCoins);

    // loop thru all todays coins
    // compare to yesterdays coins
      // loop thru array of today and compare to yesterday and add trend
  })

// loop through yesterdays coins

const startYest = new Date(Date.now());
startYest.setHours(0, 0, 0, 0);
const oneDayAgo = startYest.getDate() - 1;
startYest.setDate(oneDayAgo);

const endYest = new Date(Date.now());
endYest.setHours(23, 59, 59);
const endYestDayAgo = endYest.getDate() - 1;
endYest.setDate(endYestDayAgo);
const yesterdayCoins = await Coin.find({
  category: {
    $regex: new RegExp(fullName, "i"),
  },
  createdAt: {
    $gte: startYest,
    $lt: endYest,
  }
}).lean()
);
julz oh
  • 285
  • 6
  • 17
  • MongoDB doesn't allow you to add a property dynamically to a Model, My recommendation is, The first time you create a Model, add `Trend` property to it with a default value, and after calculates the difference just update `Trend` value. – Sharzad Gh Aug 18 '21 at 06:45
  • I'm fully unfamiliar with mongoose, but can it be done with `$addFields` and aggregation: https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/? – dododo Aug 18 '21 at 16:44

2 Answers2

3

As Sharrzard Gh said, MongoDB doesn't allow you to add a new property to your model after it's been defined. Instead, use a structure like this, to define an initially empty property that you will use later to store the trend data.

const CoinSchema = new Schema({
  specName: { type: String, required: true },
  fullName: { type: String, required: true },
  category:{ type: String, required: true },
  coinName: { type: String, required: true },
  trend: { type: String, required: true, default: '' }  // <- Add this property
});

module.exports = mongoose.model("coins", CoinSchema);

You can change the trend data type to an array or object if you need to store more complex data or a series of data points for historical trend tracking, if you want.

Try something like this:

trend: { type: Object, required: true, default: {} }

or

trend: { type: [String] , required: true, default: [] }
I Stand With Israel
  • 1,971
  • 16
  • 30
  • Does this work also? I'm completely new to MongoDB https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/ Also do I need to do a nested loop to loop through todays coins, and the loop through the array in that coin? How can I compare to yesterdays coins? Sorry.. I am still junior and cannot figure out – julz oh Aug 19 '21 at 00:00
  • So, you can't add a new property to the model after it's been defined. Instead you create an empty property, then add your calculation there, for tracking. Here is how you do a nested loop with map in React. https://stackoverflow.com/questions/47402365/how-to-have-nested-loops-with-map-in-jsx You may need either a map or a forEach. Maps return an array, forEach does not. Comparison in JS looks like this https://www.w3schools.com/js/js_comparisons.asp – I Stand With Israel Aug 19 '21 at 21:10
  • @julzoh If my answer helped you, please accept it (little green checkbox on the side) and award me the bounty you placed on your question.. This helps encourage people, like myself, to spend time writing answers for questions like yours. – I Stand With Israel Aug 25 '21 at 06:31
0

I was able to add new properties in the model even after the creation of the model in Node.js using:

insertMany(table_data, { strict: false });

where table_data container the old data appended with the new properties. The strict: false did the magic.

Order Model in Node:

const mongoose = require ('mongoose');

const OrdersSchema = new mongoose.Schema({
  sr_no :{type: Number, required:true},
  customer_name :{type: String, required:true},
  product_name: String,
  codes: String,
  code_date:{
    type:Date,
    default: Date.now()
  },
  design : String,
  design_date :{
    type:Date,
    default: Date.now()
  },
  design_approval :{
    type:String,
    default: ''
  },
  design_approval_date :{
    type:Date,
    default: Date.now()
  },
  send_to_printer :{
    type:String,
    default: ''
  },
  send_to_printer_date :{
    type:Date,
    default: Date.now()
  },
  proof_approval :{
    type:String,
    default: ''
  },
  proof_approval_date :{
    type:Date,
    default: Date.now()
  },
  shipping : String,
  ship_date :{
    type:Date,
    default: Date.now()
  },
  received :{
    type:String,
    default: ''
  },
  received_date :{
    type:Date,
    default: Date.now()
  },
  completed : String,
  notes : String,
  printing :{
    type:String,
    default: ''
  },
  printing_date :{
    type:Date,
    default: Date.now()
  },
  stapling :{
    type:String,
    default: ''
  },
  stapling_date :{
    type:Date,
    default: Date.now()
  },
  user_id :{type: Number, required:true}
},{strict: false});

OrdersSchema.index({ sr_no: -1 });

const Orders = mongoose.model(
  'Orders',
  OrdersSchema
);

module.exports = Orders;

In Mongo Compass the first record appeared like: enter image description here

And another record in the same collection was:

enter image description here