These are chat archives for Automattic/mongoose

22nd
Aug 2018
Mihajlo Ilijić
@Pritilender
Aug 22 2018 08:05

@dimitardanailov I've never seen output like yours before except when using console.log. Could it be that you're not awaiting the promise returned from findOne? Can you paste the code you're using that renders this output?

Anyhow, I'll try to answer to your question regarding removal of the attributes:

  1. Use .select() to tell Mongo which properties you want to retrieve from the DB. This methods needs either space separated or comma separated string of property names which you want to include with your model. For instance, if you want only progress and applicationId returned from your model, you would call QuerySchema.findOne(someQuery).select('progress applicationId') (someQuery is the query you're using).
  2. Another option is to not return model from findOne but to return JSON directly by using .lean(): QuerySchema.findOne(someQuery).lean(). This method will not create Mongoose model from the document returned from the DB and will return plain JSON representing the document. Using lean is good if you need high performance (there's no serialization to Mongoose model which leads to faster execution time and smaller memory footprint) but comes with a drawback that you can't use model methods because you don't have a model (duh).

Note that you can combine select and lean because they both return Mongoose's Query object.

imjeen
@imjeen
Aug 22 2018 13:13

@Pritilender I have a similar puzzle,when I get the Query object,example:

let queryData = UserModel.findOneAndRemove({ uid: id });
if(queryData){
   // how do I use lean() ??
   let data = queryData.lean();  // but it is wrong
}

how do I use the lean methond, and then get the js plain JSON. (sorry that I am a beginner )

Mihajlo Ilijić
@Pritilender
Aug 22 2018 13:25

@imjeen data is Mongoose Query object in your case. You should either pass a function that should accept the result using .exec or you should use .then or await because it returns a promise:

  1. Using exec

    UserModel
    .findOneAndRemove({uid: id})
    .lean()
    .exec((err, data) => /* data is now JSON */)
  2. Using .then (promise based #1):

    UserModel
    .findOneAndRemove({ uid: id })
    .lean()
    .then(data => /* data is now JSON */)
  3. Using async/await (promise based #2, requires ES2017 I think):

    (async () => {
    const data = await UserModel
     .findOneAndRemove({ uid: id })
     .lean()
    // data is now JSON
    })()

    Note that I used IIFE here to be able to use async/await. If you're having this inside a function, then you can just make a function async:

    const removeUserAndDoSomething = async (id) => {
    const data = await UserModel
     .findOneAndRemove({ uid: id })
     .lean()
    // data is now JSON
    }
I personally prefer using async/await if possible (it's available natively in Node 8+).
It's a lot cleaner and more straightforward syntax, but it boils down to promises.
@imjeen Btw, what's the point of if (queryData) ...?
Mihajlo Ilijić
@Pritilender
Aug 22 2018 13:31
I suggest reading Query docs and lean docs for more info.
imjeen
@imjeen
Aug 22 2018 13:33
@Pritilender Yes, it’s very clean. thank you very much. my latest code below:
@Delete("/:id")
    async delete(@Param("id") id: number) {
        let [err, data] = await utils.to(
            UserModel.findOneAndRemove({ uid: id }) //  MongoDB findAndModify()
                .select("-_id -__v")
                .lean()
        );
        data = data || {};
        if (err) throw "error";
        return {
            message: "OK",
            data: data
        };
    }
Mihajlo Ilijić
@Pritilender
Aug 22 2018 13:34
What's utils.to?
imjeen
@imjeen
Aug 22 2018 13:37
it is just for async/await error handling without try catch
@Pritilender it is:
to = (promise: any) =>
    promise.then((data: any) => [null, data]).catch((err: Error) => [err]);
Mihajlo Ilijić
@Pritilender
Aug 22 2018 13:40
TIL: You can have default values in tuples, like const [err, data = {}] = await utils.to....
So maybe you don't need data = data || {}
imjeen
@imjeen
Aug 22 2018 13:41
@Pritilender it’s a good idea :)
Mihajlo Ilijić
@Pritilender
Aug 22 2018 13:42
Anyhow, I'd argue that if findOneAndRemove returns null shouldn't be message: "OK", because the user said "Remove this one" but you couldn't find it. I'd rather error out and return 404 instead. But that's just me.
imjeen
@imjeen
Aug 22 2018 13:45
yeah, I am doing that.
Mihajlo Ilijić
@Pritilender
Aug 22 2018 13:49
Unrelated question: are you using Nest as BE framework?
imjeen
@imjeen
Aug 22 2018 13:51
no, I use the npm package: routing-controllers
it is a good way to use the Nest ? and I don’t know it before, I am just a beginner.
Mihajlo Ilijić
@Pritilender
Aug 22 2018 13:57
It's similar. I never used it but I wanted to ask for your opinion on it. Nvm :)
Karim Ellaisy
@Vikaton
Aug 22 2018 16:50
Is there a reason why I can't access a specific field in an object that's defined in the schema?
it returns undefined everytime I try to access it but can only access it using 'toObject()' method
Uday singh
@udaysinghkushwah
Aug 22 2018 17:05
how to sort data using populated file can you guys help me this is my code.
wait to(Driver.find().limit(5).populate({path:"user",options: { sort: { userRole: 1 }},options: { sort: { userRole: 1 }, limit: 5 }, populate: { path: 'country'}}));
Kev
@lineus
Aug 22 2018 17:07
@Vikaton can you share your schema and the path you expect to match?
Uday singh
@udaysinghkushwah
Aug 22 2018 17:09

`
let UserSchema = mongoose.Schema({
userFirstName: { type: String },
userLastName: { type: String },
userPhone: {
type: String,
lowercase: true,
trim: true,
index: true,
unique: true,
sparse: true, //sparse is because now we have two possible unique keys that are optional
validate: [validate({
validator: 'isNumeric',
arguments: [7, 20],
message: 'Not a valid phone number.',
})]
},
userEmail: {
type: String,
lowercase: true,
trim: true,
index: true,
unique: true,
sparse: true,
validate: [validate({
validator: 'isEmail',
message: 'Not a valid email.',
}), ]
},
country: { type: Schema.Types.ObjectId, ref: 'Countries' },
state: { type: Schema.Types.ObjectId, ref: 'States' },
city: { type: Schema.Types.ObjectId, ref: 'Cities' },
workId: { type: String },
userPassword: { type: String },
userAuthKey: { type: String },
userResetToken: { type: String },
userPicture: { type: String },
userDeviceType: { type: String },
userDeviceToken: { type: String },
userLastLogin: { type: Date },
userRole: { type: String },
selfRegister: { type: String },
userIsLoggedIn: { type: Boolean },
userOTP: { type: String },
userOTPGenerationTime: { type: Date },
companyRegistrationNo: { type: String },
advertiseYourself: { type: String },
gender: { type: String },
selfieImage: { type: String },
documentImage: { type: String },
userState: { type: String },
userAddress: { type: String },
userStatus: { type: String },
frontDisplay: { type: String },
userUnreadMessageCount: { type: Number },
userScanBalanceAmount: { type: Number },
userClassLicence: { type: Schema.Types.ObjectId, ref: 'ClassLicence' },

}, { timestamps: true });
`
this is user model

and driver have user id
i am doing query like this and i want to sort based on userRole
[err, driver] = await to(Driver.find().limit(5).populate({path:"user",options: { sort: { userRole: 1 }},options: { sort: { userRole: 1 }, limit: 5 }, populate: { path: 'country'}}));
Kev
@lineus
Aug 22 2018 17:12
@udaysinghkushwah you can surround your code with three backticks on a line:
```
code here
```
Uday singh
@udaysinghkushwah
Aug 22 2018 17:13
let UserSchema = mongoose.Schema({
userFirstName: { type: String },
userLastName: { type: String },
userPhone: {
type: String,
lowercase: true,
trim: true,
index: true,
unique: true,
sparse: true, //sparse is because now we have two possible unique keys that are optional
validate: [validate({
validator: 'isNumeric',
arguments: [7, 20],
message: 'Not a valid phone number.',
})]
},
userEmail: {
type: String,
lowercase: true,
trim: true,
index: true,
unique: true,
sparse: true,
validate: [validate({
validator: 'isEmail',
message: 'Not a valid email.',
}), ]
},
country: { type: Schema.Types.ObjectId, ref: 'Countries' },
state: { type: Schema.Types.ObjectId, ref: 'States' },
city: { type: Schema.Types.ObjectId, ref: 'Cities' },
workId: { type: String },
userPassword: { type: String },
userAuthKey: { type: String },
userResetToken: { type: String },
userPicture: { type: String },
userDeviceType: { type: String },
userDeviceToken: { type: String },
userLastLogin: { type: Date },
userRole: { type: String },
selfRegister: { type: String },
userIsLoggedIn: { type: Boolean },
userOTP: { type: String },
userOTPGenerationTime: { type: Date },
companyRegistrationNo: { type: String },
advertiseYourself: { type: String },
gender: { type: String },
selfieImage: { type: String },
documentImage: { type: String },
userState: { type: String },
userAddress: { type: String },
userStatus: { type: String },
frontDisplay: { type: String },
userUnreadMessageCount: { type: Number },
userScanBalanceAmount: { type: Number },
userClassLicence: { type: Schema.Types.ObjectId, ref: 'ClassLicence' },

}, { timestamps: true });

this is user model
and driver have user id
i am doing query like this and i want to sort based on userRole

[err, driver] = await to(Driver.find().limit(5).populate({path:"user",options: { sort: { userRole: 1 }},options: { sort: { userRole: 1 }, limit: 5 }, populate: { path: 'country'}}));

guys help me i am new in node and mongodb

Uday singh
@udaysinghkushwah
Aug 22 2018 17:20
@lineus help me
Kev
@lineus
Aug 22 2018 17:21
@udaysinghkushwah what does this query return now?
Uday singh
@udaysinghkushwah
Aug 22 2018 17:22
sorting not applied
Kev
@lineus
Aug 22 2018 17:24
at first glance, it looks like you're passing in the options property twice in the same object.
Uday singh
@udaysinghkushwah
Aug 22 2018 17:26
@lineus
```

@lineus

 [err, driver] = await to(Driver.find().populate({path:"user",select: '-userPassword -userAuthKey -userDeviceType',options: { sort: { userRole: -1 }}, populate: { path: 'country'}}));

this is query

Karim Ellaisy
@Vikaton
Aug 22 2018 17:27
analytics: {
      CURRENT_FOLLOWERS: Number,
      STARTING_COUNTS: [{
        DAY: Number,
        FOLLOWS: [Number],
        DATE: [String]
      }, {
        WEEK: [Number],
        FOLLOWS: [Number],
        DATE: [String]
      }, {
        MONTH: [Number],
        FOLLOWS: [Number],
        DATE: [String]
      }],
      DAILY_FOLLOWERS: [{
        HOUR: Number,
        HOURS: [Number]
      }],
      WEEKLY_FOLLOWERS: [{
        DATE: Number,
        DAYS: [Number],
        PERCENT_LIKES: String,
        PERCENT_FOLLOWERS: String,
        PERCENT_COMMENTS: String
      }],
      MONTHLY_FOLLOWERS: [{
        DATE: String,
        count: Number,
        FOLLOWERS: [Number],
        LIKES: [Number]
      }],
      WEEKLY_LIKES: [{
        DATE: String,
        DAYS: [Number]
      }],
      PERCENTAGES: [{
        PERCENT_LIKES: Number,
        PERCENT_FOLLOWERS: Number,
        PERCENT_COMMENTS: Number
      }]
    }
  }
Thats my schema
I cant access analytics.STARTING_COUNTS[2].MONTH
printing analytics.STARTING_COUNTS[2] shows the correct object, but accessing .MONTH returns undefined
and accessing analytics.STARTING_COUNTS[2].FOLLOWS[0] returns 'number'
is there something wrong with how im setting up the schema?
Kev
@lineus
Aug 22 2018 17:33
@Vikaton yeah, you can only define one element in the array for STARTING_COUNTS.
Karim Ellaisy
@Vikaton
Aug 22 2018 17:34
@lineus so best bet is to make those array elements into seperate object properties instead?
within STARTING_COUNTS
Kev
@lineus
Aug 22 2018 17:34
this example shows that everything past the first element is ignored.
yeah, or you could use an embedded discriminator
Karim Ellaisy
@Vikaton
Aug 22 2018 17:41
@lineus Thank you
Kev
@lineus
Aug 22 2018 17:43
anytime :)
Kev
@lineus
Aug 22 2018 18:10
@udaysinghkushwah is your user path in Driver an array?
Karim Ellaisy
@Vikaton
Aug 22 2018 18:10
@lineus it seems though that I cannot edit DAILY_FOLLOWERS.HOURS individually, so I cant do HOURS[2] = 4 but I can do HOURS = [4]
Do I need to set a fixed length in the Schema do edit the array individually ?
Kev
@lineus
Aug 22 2018 18:25
@udaysinghkushwah here's an example gist it shows that sorting works as expected when there's an array of results to sort.
@Vikaton are you creating a default array in DAILY_FOLLOWERS.HOURS when you create a new doc?
Karim Ellaisy
@Vikaton
Aug 22 2018 18:28
@lineus yeah, array of length 13 full of nulls
Kev
@lineus
Aug 22 2018 18:32
and you don't get a cast error for an array of nulls being cast to [Number]?
Karim Ellaisy
@Vikaton
Aug 22 2018 18:40
@lineus I dont cast it specifically, I took the whole analytics object and assigned it to DB
so the array of nulls is within that entire object, but would it make a difference if the type was Mixed? I just dont know why editing an element doesnt actually update
Kev
@lineus
Aug 22 2018 18:45
oh I see what you mean
I think that's the first question on the FAQ
you can either use .set(index, value) or set it manually and call markModified() on the path.
Karim Ellaisy
@Vikaton
Aug 22 2018 19:36
thanks again!
Kev
@lineus
Aug 22 2018 20:10
happy to help :+1: