I have this schema:
var orderSchema = new mongoose.Schema({
history: [{
"type": {
type: String,
enum: [
'ORDER_HISTORY_DRIVER_DETAILS',
'ORDER_HISTORY_LOADING',
'ORDER_HISTORY_LOCATION',
'ORDER_HISTORY_UNLOADING'
],
required: true
},
date: {
type: Date
},
state: {
type: String,
enum: [
'ORDER_HISTORY_STEP_STATE_COMPLETED',
'ORDER_HISTORY_STEP_STATE_CURRENT',
'ORDER_HISTORY_STEP_STATE_FUTURE',
],
default: 'ORDER_HISTORY_STEP_STATE_FUTURE',
required: true
}
}]
})
At one point, I need to remove all subdocuments that have a type of "ORDER_HISTORY_LOCATION", so I'm running this:
let result = await Order.findOneAndUpdate(
{orderId: req.params.orderId},
{
$pull: {
history: {type: "ORDER_HISTORY_LOCATION"}
}
}, {new: true}
);
When i log "result.history" i get this:
CoreMongooseArray [
{ state: 'ORDER_HISTORY_STEP_STATE_CURRENT',
_id: 5caf8a41641e6717d835483d,
type: 'ORDER_HISTORY_DRIVER_DETAILS' },
{ state: 'ORDER_HISTORY_STEP_STATE_FUTURE',
_id: 5caf8a41641e6717d835483c,
type: 'ORDER_HISTORY_LOADING',
date: 2019-05-08T09:00:00.000Z },
{ state: 'ORDER_HISTORY_STEP_STATE_FUTURE',
_id: 5caf8a41641e6717d835483b,
type: 'ORDER_HISTORY_LOADING',
date: 2019-05-09T09:00:00.000Z },
{ state: 'ORDER_HISTORY_STEP_STATE_FUTURE',
_id: 5caf8a41641e6717d8354837,
type: 'ORDER_HISTORY_UNLOADING',
date: 2019-05-13T09:00:00.000Z } ]
What is this "CoreMongooseArray"? I can't do anything with it. I also can't find any documentation on it.
CoreMongooseArray seems to be inheriting the Array type and has almost the same behavior.
Source code (at the time of writting) : https://github.com/Automattic/mongoose/blob/3e523631daa48a910b5335c747b3e5d080966e6d/lib/types/core_array.js
In case you want to convert it to a simple array, just do this :
const history = Array.from(...result.history)
Beware, if this array contains objects, each object will have undesirable additional Mongoose properties, as they are Mongoose schemas documents. You will need to convert them into plain JavaScript objects :
const history = Array.from(...result.history).map(v => v.toJSON())
Hope it helps.
This worked for me!
const history = Array.from([...result.history])
Related
how can I overwrite the value of officialLyric with the value of updateLyric??
artist: { type: String, required: true },
title: { type: String, required: true },
officialLyric: { type: String, required: true },
editedLyrics: [
{
updatedLyric: String,
status: {
type: String,
enum: ["Aproved", "Rejected", "Pending"],
default: "Pending",
},
userId: { type: Schema.Types.ObjectId, required: true, ref: "User" },
},
],
releaseDate: { type: Date },
see image for clear depiction of the question.
enter image description here
You can try update with aggregation pipeline starting from MongoDB 4.2,
$arrayElemAt to get first value of updatedLyric from editedLyrics array and update it into officialLyric
db.collection.updateMany(
{}, // put your query
[{
$set: {
officialLyric: {
$arrayElemAt: ["$editedLyrics.updatedLyric", 0]
}
}
}]
)
Playground
If you want to always have to value of latest updateLyric in editedLyric array in officialLyric, you don't need to actually store officialLyric in DB. you can use mongoose virtual fields and remove officialLyric from schema.
LyricSchema.virtual('officialLyric').get(function () {
if(!this.editedLyrics.length) return null;
return this.editedLyrics[this.editedLyrics.length-1].updatedLyric;
});
If you still want to store the officialLyric first and then overwrite it with edited version you save. You can use hooks.
LyricSchema.post('save', async(error, doc, next) => {
if(doc.editedLyrics.length && doc.officialLyric != doc.editedLyrics[doc.editedLyrics.length-1].updatedLyric){
doc.officialLyric = doc.editedLyrics[doc.editedLyrics.length-1].updatedLyric;
await doc.save();
}
next();
});
I'm building a note taking app in which there can be as many nested notes inside one another as the user wants, Similar to folder and file structure expect here everything is a note, I have a note model inside it there is a sub_notes array, I want to be able to find a note whether it is a note at the top of the document or nested inside the sub_notes array, I'm passing the id of the note to which I want to add the next note, I have tried graphlookup but when I use the query it is returning an empty array
This is my Note model
const noteSchema = new Schema({
icon: {
type: String,
},
banner: {
type: String,
},
name: {
type: String,
required: true,
},
user_id: {
type: String,
required: true,
},
date: {
type: Date,
required: true,
default: Date.now,
},
sub_notes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "notes",
},
],
content: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "blocks",
},
],
});
module.exports = Note = mongoose.model("notes", noteSchema);
This is the query I'm using to find the nested note, The path is a dot seperated string with ids of the notes & the note is the actual note object with the name and other data.
So for ex:
The note
{
id: "123123123",
sub_notes: [{
id: "xyz"
}]
}
So the path here will be 123123123.xyz
var {
note,
path
} = req.body;
var newPath = path.split(".");
const parentNote = Note.findById(newPath[0]);
newPath.splice(0, 1);
console.log(newPath[newPath.length - 1]);
Note.aggregate([{
$match: {
$id: newPath[newPath.length - 1]
}
},
{
$graphLookup: {
from: "notes",
startWith: "$id",
connectFromField: "sub_notes",
connectToField: "id",
depthField: "level",
as: "sub_notes",
},
}
]).exec((err, notes) => {
console.log(notes);
});
I have a Schema of Project that looks like this:
const ProjectSchema = new mongoose.Schema({
name: {
type: String,
Required: true,
trim: true
},
description: {
type: String,
},
devices: [{
name: {type: String, Required: true},
number: {type: String, trim: true},
deck: {type: String},
room: {type: String},
frame: {type: String}
}],
cables: {
type: Array
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
adminsID: {
type: Array
},
createdAt: {
type: Date,
default: Date.now
}
I want to query an object from array of "devices".
I was able to add, delete and display all sub-documents from this array but I found it really difficult to get single object that matches _id criteria in the array.
The closest I got is this (I'm requesting: '/:id/:deviceID/edit' where ":id" is Project ObjectId.
let device = await Project.find("devices._id": req.params.deviceID).lean()
console.log(device)
which provides me with below info:
[
{
_id: 6009cfb3728ec23034187d3b,
cables: [],
adminsID: [],
name: 'Test project',
description: 'Test project description',
user: 5fff69af08fc5e47a0ce7944,
devices: [ [Object], [Object] ],
createdAt: 2021-01-21T19:02:11.352Z,
__v: 0
}
]
I know this might be really trivial problem, but I have tested for different solutions and nothing seemed to work with me. Thanks for understanding
This is how you can filter only single object from the devices array:
Project.find({"devices._id":req.params.deviceID },{ name:1, devices: { $elemMatch:{ _id:req.params.deviceID } }})
You can use $elemMatch into projection or query stage into find, whatever you want it works:
db.collection.find({
"id": 1,
"devices": { "$elemMatch": { "id": 1 } }
},{
"devices.$": 1
})
or
db.collection.find({
"id": 1
},
{
"devices": { "$elemMatch": { "id": 1 } }
})
Examples here and here
Using mongoose is the same query.
yourModel.findOne({
"id": req.params.id
},
{
"devices": { "$elemMatch": { "id": req.params.deviceID } }
}).then(result => {
console.log("result = ",result.name)
}).catch(e => {
// error
})
You'll need to use aggregate if you wish to get the device alone. This will return an array
Project.aggregate([
{ "$unwind": "$devices" },
{ "$match": { "devices._id": req.params.deviceID } },
{
"$project": {
name: "$devices.name",
// Other fields
}
}
])
You either await this or use .then() at the end.
Or you could use findOne() which will give you the Project + devices with only a single element
Or find, which will give you an array of object with the _id of the project and a single element in devices
Project.findOne({"devices._id": req.params.deviceID}, 'devices.$'})
.then(project => {
console.log(project.devices[0])
})
For now I worked it around with:
let project = await Project.findById(req.params.id).lean()
let device = project.devices.find( _id => req.params.deviceID)
It provides me with what I wanted but I as you can see I request whole project. Hopefuly it won't give me any long lasting troubles in the future.
I have a MongoDB collection that allows to store videos rankings, here is the schema:
var ChallengeVideoRankingSchema = new Schema({
_challenge: {
type: ObjectId,
ref: 'Challenge',
},
since: {
type: String,
enum: ['all', 'day', 'week', 'month']
},
type: {
type: String,
enum: ['won_duels', 'diamonds']
},
total: {
type: Number
},
challengeVideos: [
{
rank: {
type: Number
},
_challengeVideo: {
type: ObjectId,
ref: 'ChallengeVideo'
}
}
],
creation_date: {
type: Date,
default: Date.now
},
});
I would like to make a request with mongoose which allows me to retrieve some of the ChallengeVideo objects in one of the rankings to make a pagination. I would like for example to have in the field "challengeVideos", 20 objects representing the ranks 1-20, 21-40, 41-60, ... according to a given parameter.
Thank you in advance,
Bastien
Get last elements with slice
db.posts.find( {}, { comments: { $slice: -3 } } )
link https://www.mongodb.com/docs/manual/reference/operator/projection/slice/#return-an-array-with-its-last-3-elements
Consider this command:
WorkPlan.findOneAndUpdate({ _id: req.params.id }, updateObj, function(err) {
...
})
versus this:
WorkPlan.findOneAndUpdate({ _id: req.params.id }, { '$set': updateObj }, function(err) {
...
})
While developing my project, I was surprised to find out that the result of the first command is the same as the result of the second command: the updateObj is merged into the existing record in the database, even in the first case when it is supposed to replace it. Is this a bug in mongoose/mongodb or am I doing something wrong? how can I replace an object on update instead of merging it? I'm using mongoose 4.0.7.
Thanks.
==========
Update:
This is the actual WorkPlan schema definition:
workPlanSchema = mongoose.Schema({
planId: { type: String, required: true },
projectName: { type: String, required: true },
projectNumber: { type: String, required: false },
projectManagerName: { type: String, required: true },
clientPhoneNumber: { type: String, required: false },
clientEmail: { type: String, required: true },
projectEndShowDate: { type: Date, required: true },
segmentationsToDisplay: { type: [String], required: false },
areas: [
{
fatherArea: { type: mongoose.Schema.ObjectId, ref: 'Area' },
childAreas: [{ childId : { type: mongoose.Schema.ObjectId, ref: 'Area' }, status: { type: String, default: 'none' } }]
}
],
logoPositions: [
{
lat: { type: Number, required: true },
lng: { type: Number, required: true }
}
],
logoPath: { type: String, required: false },
}, { collection: 'workPlans' });
WorkPlan = mongoose.model('WorkPlan', workPlanSchema);
And this is an example of updateObj:
var updateObj = {
projectManagerName: projectManagerName,
clientEmail: clientEmail,
clientPhoneNumber: clientPhoneNumber,
segmentationsToDisplay: segmentationsToDisplay ? segmentationsToDisplay.split(',') : []
}
Therefore, when I'm NOT using the $set flag, I would expect the field projectNumber, for example, not to exist in the new record, yet I see it is still there.
Mongoose update treats all top level keys as $set operations (this is made more clear in the older docs: Mongoose 2.7.x update docs).
In order to get the behavior you want, you need to set the overwrite option to true:
WorkPlan.findOneAndUpdate({ _id: req.params.id }, updateObj, { overwrite: true }, function(err) {
...
})
See Mongoose Update documentation
In addition to the answer above:
[options.overwrite=false] «Boolean» By default, if you don't include
any update operators in doc, Mongoose will wrap doc in $set for you.
This prevents you from accidentally overwriting the document. This
option tells Mongoose to skip adding $set.
Link to docs: https://mongoosejs.com/docs/api.html#model_Model.update
This is works for me $set in Mongoose 5.10.1,
WorkPlan.where({ _id: req.params.id }).updateOne(updateObj);
Note:if you have inner object then give exact path of each key in updateObj
example:
"Document.data.age" = 19
ref: https://mongoosejs.com/docs/api.html#query_Query-set