I am trying to get data from mongoDB with find() function, which should returns objects in array form. But I cannot get the data I want in the array as it returns undefined.
This is the Object Array:
[
{
_id: new ObjectId("635fa2d24f33bf4626211990"),
timestamp: '2022-10-30T08:41:06.826Z',
content: 'something here',
published: 'false'
}
]
let data = await submissionSchema.find({ published: "false" }).exec();
I have defined data as the response coming out from the database, which returns the Object Array above. By console.log(data[0]) it shows everything fine without the [] bracket. When I console.log(data[0].content), it returns undefined, but I supposed it to have something here in the console. Anyone have clues on it? This will be greatly appreciated.
I have finally figured out where the problem comes from. I will be describing below so that whoever might have the same problem can know where the issues comes from.
submissionSchuema:
const mongoose = require('mongoose');
const reqString = {
type: String,
require: true
}
const submissionSchema = mongoose.Schema({
remark: reqString,
published: reqString
})
module.exports = mongoose.model('submission-records', submissionSchema)
The issue is happening since I didn't put the data I wish to get into the Schema. Therefore, the bug can be simply fixed by adding the data you are looking for back to the schema.
const submissionSchema = mongoose.Schema({
_id: reqString,
timestamp: reqString,
content: reqString,
remark: reqString,
published: reqString
})
Related
Using the model.find method I can find documents that match all properties defined within my schema except when a property has a type of mongoose.Schema.ObjectId. In my case, references to the owner of the document. I've tried all sorts of ways to get documents by the owner _id but nothing is working. Anybody know the proper way to do this ?
--EDIT--
Just in case anybody runs into the same issue, I got it working using the following query.
Query:
const users_cars = await Cars.find( { owner: user_id } );
Schema:
const Schema = new mongoose.Schema({
type: Object,
owner: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: [ true, 'car must belong to an owner' ]
},
name: String
});
I am trying to make a messages schema + routes for my backend. I want that two users can write a message to each other and the message has to be stored for both of them.
I made the the user-model-schema and the user-routes, they are working but I'm stuck with the messaging.
mongoDB should contain the message
how can I manage sending messages?
Here is what I tried so far
messages-route:
var express = require("express");
var User = require("../models/users.js");
var router = express.Router();
const message = require("../models/messages");
router.post("/:recipient", (request, response) => {
User.find({
username: [request.body.sender, request.params.recipient],
// }, {
// message: request.body.message
// }, {
// upsert: false,
// new: true,
})
.then((users) => {
users.forEach((user) => {
user.updateMany({
message: request.body.message
})
})
response.status(200).json(users);
})
.catch((error) => {
response.status(500).json(error);
});
});
module.exports = router;
and my messages-schema:
var mongoose = require("mongoose");
var UserMessage = new mongoose.Schema({
user: { "type": mongoose.Schema.ObjectId, "ref": "User" },
username: String,
view: {
inbox: Boolean,
outbox: Boolean,
archive: Boolean
},
content: {type: String},
read: {
marked: { "type": Boolean, default: false },
date: Date
}
});
var schemaMessage = new mongoose.Schema.ObjectId({
from: String,
to: [UserMessage],
message: String,
created: Date
});
module.exports = mongoose.model("Messages", UserMessage);
I'm very unsure with the schema, I put in some suggestions I found here on stackoverflow.
Thanks!
I'm now making a messages Schema, and i thought of something like this:
const ChatSchema = new mongoose.Schema({
participants: [{type: mongoose.Schema.objectId, ref: "users"}],
last_message: Number,
_id: {type: mongoose.Schema.objectId}
//...
})
Here you could use the same schema to make chat groups, and it will work having a separate document for each message with a field with the chatroom _id, so you could query them
And you might be thinking, why not put all the messages in an array on ChatSchema?
Well, because we don't want the users to recieve all their messages each time they enter a chat.
I know, you might be thinking about something like this to prevent that over-load of data to the client
MessageSchema.find(({ ... })
.sort({ updatedAt: -1 })
.limit(20)
But here you're still recieving all the messages from the database to the server, and users could have even 1000 messages per chat, so an idea that came to my mind was making MessageSchema like this:
const MessageSchema = new mongoose.Schema({
message: String,
chatRoom_id: {type: mongoose.Schema.objectId},
sentBy: {type: mongoose.Schema.objectId, ref: "users"},
seenBy: [{ user: {type: mongoose.Schema.objectId, ref: "users"}, seen: Boolean }],
numberOfMessage: Number
//...
})
And the magic is with numberOfMessage, because you could query the MessageSchema with comparison operators like this:
const currentMessage = 151 //this is the numberOfMessage number of the last message the user saw, and when the user scrolls up the chat you could send the numberOfMessage of the last loaded message.
//To know from wich numberOfMessage start, just use the ``last_message`` field from the chatroom's ``ChatSchema``
const quantityNewMessages = 10 //this is the quantity of new messages you want the user to recieve
MessageSchema.find({
chatRoom_id,
numberOfMessage:
{ $lt: currentMessage , $gte: currentMessage + quantityNewMessages } //we use less than operator because the user will always recieve the last message first, so the previous messages are going to have a numberOfMessage less than this first message
})
And it will return us an array of 10 messages, wich you can concat to the messages array you already have in your frontend.
take note:
With this MessageSchema, all the users will share the same document for each message, you could make a separate document of a message for each user in a chatroom, so each user can delete a message without affecting the others.
I don't recommend you saving a username field, because the user could change it's username and if he does that, you would have to update ALL message documents with that username to the new username, so just leave the ref and populate it
You shouldn't put the document object in the to field, make it a separate document and only save it's ref. But I don't recommend doing it that way either
Don't use users.forEach because it modifies the array, use users.map because it returns a new array and doesn't mutates the original array
I see you have a bug, you are asking the data with the returned variable of the forEach( user => user.update(...) ), it should be map( user => UserSchema.update({ _id: user._id }) )
But still, looping an array and making a call to the DB each time is very expensive and will lag the server, so use something like the ChatSchema I showed you, because you could get the chatroom information, and with the _id of that chatroom, query the newest messages
I've not tested this code, i'm about to do it. If you run into any problem feel free to comment
I have an Express API in which I have the following Mongoose query that extracts posts from the database and then I want to convert the timestamp from the database (a Date() object) into a relative time string.
As you can see, I am trying to add a time property that has this string as a value to the posts object using Array.Map.
That seems to work, because logging items[0].time in the console returns the proper value (see comment in the cose).
HOWEVER! when sending the object back with res.json, the time property is not in it.
I thought this might be a client-side cache issue, but when adding another value in res.json, the new value gets sent along with the posts just fine.
Post.find({}, 'author text timestamp')
.sort({ _id: -1 })
.populate({ path: 'author', select: 'username' })
.exec(function(error, posts) {
if (error) {
console.error(error)
}
items = posts.map(function(item) {
item.time = moment(item.timestamp).fromNow()
return item
})
console.log('Relative date:' + items[0].time) // This logs: "Relative date:an hour ago"
res.json({
posts: items
})
/*
Response:
posts: {
0: {
author: {_id: "5c98f40f793edf61bcc94b4d", username: "Admin"},
text: "Why",
timestamp: "2019-04-04T15:46:36.142Z",
_id: "5ca626dc45734a2612acbcd2"
}
}
*/
})
Is this a server-related cache issue or something unique to Mongoose objects that I don't know about?
Thanks in advance for the help.
I was able to solve this using Post.find().lean() in my code.
I'm fairly new to MongoDB and have started to learn about relationships. I'm running into an issue where i'm trying to store an array of objects into one of my collection models. Basically the array is storing fine in the sense that if I look at it in compass all the correct information is there. The issue lies with retrieving the array, here is looks like the object in my array has been flattened to a string as it's just retrieving somthing like this:
Note: i'm using node Express as my BE framework
members: [ [Object] ] } ]
Here is my model below:
const teamSchema = new Schema({
name:{
type:String,
trim:true,
required:'Please enter a team name'
},
owner:{
type:mongoose.Schema.ObjectId,
ref:'User'
},
members:{
type:Array,
}
});
And here is how I use the model in Node:
const team = new Team({
name:req.body.teamName,
owner:req.user._id,
members:[{
id:req.user._id,
privilege:'admin'
}]
})
try {
await team.save();
} catch(error) {
console.log(error)
}
Here is what it looks like stored in MongoDB Compass
But when retrieving it and trying to modify it like so:
const team = await Team.find({'_id':teamId});
finds it no problem but the members array just displays as what I mentioned before:
members: [ [Object] ] } ]
can anyone tell me where I might be going wrong? Think it might be to do with the way i'm saving it rather than retrieving it
Try to use the projection field of the findto return the array in the searched element:
const team = await Team.find({'_id':teamId}, {'members': 1});
Let's say we have a Mongoose schema in our Node.js project:
let coolSchema = new mongoose.Schema({
field_1 : Number,
field_2 : String,
field_3 : [ String ],
});
And let's we have an according object:
var data = {
field_1 : 123,
field_2 : 'blah',
field_3 : ['aa', 'bb'],
};
Now to save this data into MongoDB we can use this code:
let Model = require('mongoose').model('CoolModel', coolSchema);
(new Model(data)).save();
Ok, while it's all cool.
But if data does not contain field_3 (array field, and the same will be for an object field) Mongoose will anyway add this field into the being created document with empty value.
Can we somehow tell Mongoose not to create this field if it's not contained in the data object?
you can do it easily skip the array field and array of object field.. This will let you skip saving empty array in new documents.but you have to use pre hook for this .
var brandSchema = new Schema({
name : {type:String},
email:String,
check:[]
})
brandSchema.pre('save', function (next) {
if (this.isNew && 0 === this.check.length) {
this.check = undefined;
}
next();
})
when new document is inserted in your schema you have to use this middlware.this works fine so try this.
this is the response when we want to insert any document
"data": {
"__v": 0,
"name": "testing",
"email": "testing#gmail.com",
"_id": "5915b018e292833edda8837f"
}
so i have send only email and name but check(array) field is skipped(Not send any value).
The accepted answer is good. But if you wouldn't want to use pre-hook, then you can add default: undefined to the array fields. For example:
var schema = new Schema({
myArr: { type: [String], default: undefined }
});
Refer to this comment for more explanation.
Not particularly an answer to the question itself but some thought on the matter.
It's not clear exactly what you're trying to achieve here. You defined a schema that is supposed to contain a list of string. Mongoose correctly does so that the data saved in your schema is consistent with the definition of the schema.
In this case, the list is more of a structural part of the schema. If you have different behaviour, you'd have to handle special case in your code in case the list isn't present. Now, you can safely assume that you schema is always returning a list so fetching some data will always allow you to do:
coolData.field_3.forEach(function(x) {
do_cool_things(x)
})
What you're asking is to make the schema allow inconsistent data being returned from mongodb... In other words, you'd have to do this in order to prevent accessing attributes on undefined:
if (coolData.field_3) {
coolData.field_3.forEach(function(x) {
do_cool_things(x)
})
}
Also, I you were trying to optimize the size of you objects/database, you could fill a bug report so mongoose doesn't define empty values while saving the objects and autofill them with defaults when the field is missing from mongodb. (I could be wrong but did you actually check if the data in mongodb was containing empty values or you were just looking at data coming from mongoose?)
It's because you're not marking the fields as required in your schema definition.
Do this:
let coolSchema = new mongoose.Schema({
field_1 : { type: Number, required: true },
field_2 : { type: String, required: true },
field_3 : { type: [ String ], required: true },
});