These are chat archives for Automattic/mongoose

27th
Aug 2018
Andrey Gazhala
@AndreyGazhala
Aug 27 2018 08:56

Hi all. I have two models Article and Category. They are in relations(category: { type: Schema.Types.ObjectId, ref: 'category' }). I want to change field category to 'new' value in all Article documents when I delete 'old' category value. For this I use

// *** delete SINGLE category *** //
function deleteCategory(req, res) {
  Category.findByIdAndDelete(req.params.id, function(err, category) {
    if(err) {
      res.json(err);
    } else {
        Article.where({ category : category.id }).updateMany({ $set: {category : '5b83a8c043bf5623488d0bc6'}});

        res.json(category);
        intel.info('Deleted category ', category);
      }
  });
}

This code delete category, but don't change field category to new value in Articles

Kev
@lineus
Aug 27 2018 10:08
@AndreyGazhala what version of mongoose are you using?
Andrey Gazhala
@AndreyGazhala
Aug 27 2018 10:17
@lineus "mongoose": "^5.1.4"
Kev
@lineus
Aug 27 2018 10:50
@AndreyGazhala honestly, I don't think that your updateMany command should work, as the update doc is being passed in as the first parameter instead of as the second parameter. The Api docs don't mention that the conditions object is optional. That being said, try putting .exec() on the end of your updateMany call. That's how I got this code based on your example to work
Kev
@lineus
Aug 27 2018 11:04
in your example, you'll probably want to put the res.json/intel.info calls into a function that you pass into updateMany.exec()
Article.where({ category : category.id }).updateMany({ $set: {category : '5b83a8c043bf5623488d0bc6'}}).exec(function(err) {
        res.json(category);
        intel.info('Deleted category ', category);
});
Andrey Gazhala
@AndreyGazhala
Aug 27 2018 11:06
@lineus It worked. Thank you very much for your time. I did not expect anyone to take it so seriously. You are awesome!!
Kev
@lineus
Aug 27 2018 11:06
I'm glad I could help :)
oh, and I was wrong about updateMany. Because you're actually calling Query.prototype.updateMany, the condition or criteria object is optional :smile:
Andrey Gazhala
@AndreyGazhala
Aug 27 2018 11:13
@lineus I'm new to the mongoose. For this reason, I did not take this moment into account. Thanks again for your time, good day to you))) You make my day
Kev
@lineus
Aug 27 2018 11:16
I learned something new too :)
mongoosebot
@mongoosebot
Aug 27 2018 13:13
:tada: We just released version 5.2.10 :tada:

5.2.10 / 2018-08-27

  • fix: bump mongodb driver -> 3.1.4 #6920 #6903 #6884 #6799 #6741 Fonger
  • fix(model): track session option for save() as the document's $session() #6909
  • fix(query): add Query.getOptions() helper #6907 Fonger
  • fix(document): ensure array atomics get cleared after save() #6900
  • fix(aggregate): add missing redact and readConcern helpers #6895 Fonger
  • fix: add global option mongoose.set('useCreateIndex', true) to avoid ensureIndex deprecation warning #6890
  • fix(query): use projection option to avoid deprecation warnings #6888 #6880 Fonger
  • fix(query): use findOneAndReplace() internally if using overwrite: true with findOneAndUpdate() #6888 Fonger
  • fix(document): ensure required cache gets cleared correctly between subsequent saves #6892
  • fix(aggregate): support session chaining correctly #6886 #6885 Fonger
  • fix(query): use projection instead of fields internally for find() and findOne() to avoid deprecation warning #6880
  • fix(populate): add getters option to opt in to calling getters on populate #6844
hillct
@hillct
Aug 27 2018 15:41
Good afternoon all. I wonder if anyone could provide some guidance as to best practices in re-ordering an array of subdocuments. Is it reasonable to change the actual array order of the subdocuments? Is it better handled by specifying in each document, the next document in the output array? Is there code out there to handle the reordering of the elementsin a reasonable way? Thanks
Ben Chiciudean
@benydc
Aug 27 2018 16:19
if I create a new document variable with var user = new User({ email: ’some email'}); and then var userString = JSON.stringify(user); will I able to parse it and apply the document to it? var userParsed = new User(JSON.parse(userString)):
hillct
@hillct
Aug 27 2018 16:26
in theory, sure, assuming the parsed string has the needed elements
hillct
@hillct
Aug 27 2018 16:52
Further to my earlier question, I get the sense that I need to use a combination of $slice $position and $push to move an item up or down in an array, but can anyone point me to an example of this sort of implementation?
Charlie
@charliecode
Aug 27 2018 16:55
Hey guys, anyone know how to get the size of a document with Mongoose?
Maurizio Bellemo
@mbellemo_twitter
Aug 27 2018 19:18

Hi guys, I managed to create the aggregation I want, but I do not know how to save it to the object...

CustomerSchema.pre('findOneAndUpdate', function (next) {
    Customer.aggregate([
        {
            $match: {
                $or: [
                    { 'deals.status': 'pipeline' },
                    { 'deals.status': 'lost' }
                ]
            }
        },
        {
            $project: {
                potential: {
                    $reduce: {
                        input: "$deals",
                        initialValue: 0,
                        in: { $add: ["$$value", "$$this.value"] }
                    }
                }
            }
        }
    ]).then(result => {
        console.log(result);
        next();
    }).catch((e) => {
        console.log(e);
        next();
    });
});

The result in the console is perfect [ { _id: 5b7dc623b98d358c6da72890, potential: 3559 } ]
How can i save the attribute potential in the object (taking into consideration that is not specified in the schema)?

Kev
@lineus
Aug 27 2018 19:26
@charliecode you can get it with the bson module that gets installed along with mongoose and the native driver. There's an instance method on bson objects called calculateObjectSize(). Using bson requires querying the document from the db. here's an example
Charlie
@charliecode
Aug 27 2018 19:29
@lineus Awesome, thanks so much!
Maurizio Bellemo
@mbellemo_twitter
Aug 27 2018 19:48
another quick question... I am using a pre hook to aggregate the sum of values of the deal array... should i do it as a pre or a post hook?
Kev
@lineus
Aug 27 2018 20:19
@hillct what are you doing with the array that the order of the elements matters? Is it an array of primitives or of subdocs?
Kev
@lineus
Aug 27 2018 20:27
@mbellemo_twitter do you want the property to get stored in the DB?
Maurizio Bellemo
@mbellemo_twitter
Aug 27 2018 20:30
yes
i tried $out... but it does not work, or i do not use it properly
hillct
@hillct
Aug 27 2018 20:31
@lineus I’m allowing the user to sort a list of items related to their profile, using on the UI side, jQuery Sortable, which returns a list of the items in the new sort order. I realize there are several approaches to this… A linked list style where an item references the previous item, yielding a usable sort order, or a straight incrementing integer ID separate from the Mongo record _id, or just using the array index itself. Not sure which is considered best practive within MongoDB, or made most convenient within Mongoose. Any suggestions appreciated
hillct
@hillct
Aug 27 2018 20:37
@lineus and they’re stored as subDocuments currently. That could be changed if there’s a superior methodology to be had
Maurizio Bellemo
@mbellemo_twitter
Aug 27 2018 20:41
@lineus yes
Kev
@lineus
Aug 27 2018 20:43
@hillct I would just overwrite the entire existing array with the newly sorted array, maybe like this?
@mbellemo_twitter I think I need to go back and read your question again :)
@mbellemo_twitter is there a reason why potential can't be in the customerSchema?
Maurizio Bellemo
@mbellemo_twitter
Aug 27 2018 20:49
i can add it... would it make the difference?
Kev
@lineus
Aug 27 2018 20:51
unless you're setting strict: false it's the only way to save the value to the docs using mongoose afaik
in your .then(res) you could add a call to Customer.updateOne({ _id: someid }, { $set: { potential: res[0].potential }}) etc
Maurizio Bellemo
@mbellemo_twitter
Aug 27 2018 20:54
so basically, i just have to add the field in the schema and it will be saved by the aggregate without me doing anything?
or i still have to perform the updateOne?
Kev
@lineus
Aug 27 2018 20:56
you'll need to update the doc in the db somehow, but if the path isn't defined in the schema you won't be able to set the value through mongoose.
Maurizio Bellemo
@mbellemo_twitter
Aug 27 2018 20:58
sorry i do not get it...
i just want to save the aggregated value directly in the object. I thought that just by using the aggregate function + the $project would do
instead it does not save anything
even if i add the attribute potential in the schema
Kev
@lineus
Aug 27 2018 21:01
aggregate doesn't return a mongoose document, or even a document from the collection. it's just a plain old javascript object.
you have to call updateOne or findOneAndUpdate with the doc's id you want to update and set the property
Maurizio Bellemo
@mbellemo_twitter
Aug 27 2018 21:02
i see
Customer.aggregate([
        {
            $match: {
                $or: [
                    { 'deals.status': 'pipeline' },
                    { 'deals.status': 'lost' }
                ]
            }
        },
        {
            $project: {
                potential: {
                    $reduce: {
                        input: "$deals",
                        initialValue: 0,
                        in: { $add: ["$$value", "$$this.value"] }
                    }
                }
            }
        }
    ]).then(result => {
        console.log(result);
        Customer.updateOne({ _id: result[0]._id }, { $set: { potential: result[0].potential }})
        next();
    }).catch((e) => {
        console.log(e);
        next();
    });
something like this?
i tried to run but it does not add the property... should i maybe create a new object?
Kev
@lineus
Aug 27 2018 21:03
does that next() come from the route in express?
it should probably be something more like:
.then(result => {
        console.log(result);
        Customer.updateOne({ _id: result[0]._id }, { $set: { potential: result[0].potential }}, (err, updated) =>{
            if(err) { return handleError(err) }
              next();
        })
    })
Maurizio Bellemo
@mbellemo_twitter
Aug 27 2018 21:16
i also have another doubt... shouldn't it be a post hook instead of a pre hook?
something like...
CustomerSchema.post('findOneAndUpdate', function(customer, next) {
    if(customer && customer.deals) {
        const reducer = (accumulator, deal) => {
            if(deal.status === 'pipeline' || deal.status === 'lost') {
                return accumulator + +deal.value; 
            }
        }
        console.log(customer.deals.reduce(reducer, 0));
        Customer.updateOne({ _id: customer._id }, { $set: { potential: customer.deals.reduce(reducer, 0) }})
    }
});
it does not work anyway, but at least i would like to understand when to save the value
again the console.log prints the right value, but there is nothing saved in the DB
Maurizio Bellemo
@mbellemo_twitter
Aug 27 2018 21:21
ok forget what i said... late night, no brain left...
CustomerSchema.post('findOneAndUpdate', function(customer, next) {
   if(customer && customer.deals) {
        const reducer = (accumulator, deal) => {
            if(deal.status === 'pipeline' || deal.status === 'lost') {
                return accumulator + +deal.value; 
            }
        }
        Customer.updateOne({ _id: customer._id }, { $set: { potential: customer.deals.reduce(reducer, 0) }}).then(_ => {
            next();
        });
    }
});
I just had to wait for the Promise and call next()
now it is saved