I have a Mongoose model with embedded documents:
const ticketSchema = new mongoose.Schema({
quantity: Number,
price: Number,
date: Date
});
const eventSchema = new mongoose.Schema({
name: String,
tickets: [ticketSchema]
});
const Event = mongoose.model('Event', eventSchema);
What I want is to extract all events with Event.find({}), but in each event, I want the date and price of the most recent ticket in the particular event, so I guess it's something like
Event.aggregate({ $groupBy: { $max: { tickets.date } });
Ii know this doesn't work, but I have to use some combination of find, aggregate, and $max.
Related
I'm using Mongoose and a MongoDB.
I have three schemas user and genres and songs.
const userSchema = new Schema({
name: String,
age: String,
favorite_genres: {
type: Schema.Types.ObjectId,
ref: 'Genre'
}
});
const genreSchema = new Schema({
name: String,
songs: {
type: Schema.Types.ObjectId,
ref: 'Song'
}
});
const songSchema = new Schema({
title: String,
artist: String,
length: String,
year: String
});
those are simplifid schemas, but they will do for this puprose.
So when I query a user I want to populate the doc with the favorite genres. But there (due to the potential amount of data) not with the ids of all songs, but only the number of songs. How can I achieve that?
I tried virtuals but they don't work the way I wanted it.
What I'm looking for is sth like the following:
const user = await User.findById(userID).populate({path: 'favorite_genres', select: 'name', sizeof: 'songs'}).lean()
// or
const user = await User.findById(userID).populate({path: 'favorite_genres', select: ['name', 'songs.length']}).lean()
Does anyone have an idea?
i have 2 Schema like bellow, i need to get 50 item from videoInfos which not have in userInfos.watched (this array content _id of videoInfos). Please use syntax like videoInfos.find().exce if you can.
const userSchema = new mongoose.Schema({
username:String,
password:String,
point: Number,
watched: Array,//content _id of videoInfos
running: Array,
});
const userInfos = mongoose.model('userInfos', userSchema);
//======================================================
const videoSchema = new mongoose.Schema({
owner:String,
videoID:String,
totalTime: Number,
totalView:Number,
finish: Number,
didFinish:Boolean,
});
const videoInfos = mongoose.model('videoInfos', videoSchema);
Since you are using mongoose you can achieve it like this.
Change the schema like this:
const userSchema = new mongoose.Schema({
username:String,
password:String,
point: Number,
watched: [{ type: Schema.Types.ObjectId, ref: 'videoInfos' }],
running: Array,
});
And query like this:
userInfos.find({}).populate('watched');
The watched array should be populated with videoInfo data.
For more, take a look at mongoose populate.
How to save .text of commentSchema as array format in the field of .comments in reviewSchema?
of course each review (post) has multiple comments.
// Connect to the db using mongoose
mongoose.connect("mongodb://localhost:27017/reviewdb")
// setting up model and schema for comment
var commentSchema = new mongoose.Schema( {
text: String,
user : String,
username: String,
date: Date
});
// setting up model and schema for reviews
var reviewSchema = new mongoose.Schema( {
text: String,
user : String,
username: String,
mydate: Date,
likes:{type: Number, "default":0, min:0},
comments:[commentSchema]
});
// setting up model for review
var Review = mongoose.model('review', reviewSchema);
// setting up model for comment
var comment = mongoose.model('comment', reviewSchema);
I'm trying to nest schemas using mongoose, but i got stuck and i don't really know why. Here is what i got.
My parent schema
const Comment = require("./Comment");
const BookSchema = new Schema({
_id: Number,
comments: [{ comment: Comment }],
ratings: [{ rate: Number }],
calculatedRating: Number
});
module.exports = Book = mongoose.model("book", BookSchema);
and child schema
const CommentSchema = new Schema(
{
userName: String,
rating: Number,
body: String,
submit_date: {
type: Date,
default: Date.now
}
},
{ _id: false }
);
module.exports = Comment = mongoose.model("comment", CommentSchema);
And with this setup im getting an error :
"TypeError: Invalid schema configuration: Model is not a valid type
at path comment."
I'm considering that i did something wrong with those exports but I'm not sure.
Your ./Comment should be:
const CommentSchema = new Schema(
{
userName: String,
rating: Number,
body: String,
submit_date: {
type: Date,
default: Date.now
}
},
{ _id: false }
);
module.exports = CommentSchema;
If you define as a new model as you did then it will create it's own collection and will be a new model instead of a sub document schema.
TL;DR
How do you reference (and thus populate) subdocuments within the same collection?
I've been trying for a while now to populate a reference to a subdocument in my Mongoose schema. I have a main schema (MainSchema) which holds arrays of locations and contacts. The locations have a reference to these contacts.
In my location array i make a reference to these contacts by the _id of the contact. See below.
import mongoose from 'mongoose';
const LocationSchema = new mongoose.Schema({
city: {type: String},
contact: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Contact' //alternative tried: refPath: 'contacts'
}
});
const Location = mongoose.model('Location', LocationSchema);
const ContactSchema = new mongoose.Schema({
firstName: {type: String},
lastName: {type: String}
});
const Contact = mongoose.model('Contact', ContactSchema );
const MainSchema = new mongoose.Schema({
name: {type: String},
locations: [LocationSchema],
contacts: [ContactSchema]
});
export.default mongoose.model('Main', 'MainSchema');
Now when i want to populate the contact of the locations I get null or just the plain _id string returned. Below is my populate code. I've tried every combination i could find, involving making the nested documents their own models and trying different ways to reference them.
MainSchema.statics = {
get(slug) {
return this.findOne({name})
.populate('locations.contact')
.exec()
.then((company) => {
if (company) {
return company;
}
const err = 'generic error message';
return Promise.reject(err);
});
}
};
I've also tried the newer approach to no avail:
populate({
path: 'locations',
populate: {
path: 'contacts',
model: 'Contact'
}
});
I must be missing something here. But what?
edited the question to show the complete query statement as requested
After searching some more i found an exact same case posted as an issue on the Mongoose github issue tracker.
According to the prime maintainer of Mongoose this form of populate is not possible:
If you're embedding subdocs, you're not going to be able to run
populate() on the array, because the subdocs are stored in the doc
itself rather than in a separate collection.
So taking your example of schema I would do the following. Please take notice that I don't say that my approach is the best way of doing this, but I had an exact similar case as you.
Mongoose model
import mongoose from 'mongoose';
const LocationSchema = new mongoose.Schema({
city: {type: String},
contact: { type: Schema.Types.ObjectId, ref: 'Contact'}
});
const ContactSchema = new mongoose.Schema({
firstName: {type: String},
lastName: {type: String}
});
const MainSchema = new mongoose.Schema({
name: {type: String},
locations: [{ type: Schema.Types.ObjectId, ref: 'Location' }],
});
const Main = mongoose.model('Main', MainSchema);
const Location = mongoose.model('Location', LocationSchema);
const Contact = mongoose.model('Contact', ContactSchema );
Note: In your main schema I've removed the contacts since I understand from your example that each location has it's own contact person, so actually in the MainSchema you don't need the ContactSchema
The controller where the you insert the data
The idea here is that you have to pass the reference _id from each document to the other one, the below example is mock-up please adapt it to fit your app. I used a data object that I assume that has one location and one person contact
async function addData(data) {
//First you create your Main document
let main = await new Main({
name: data.name
}).save();
//Create the contact document in order to have it's _id to pass into the location document
let contact = await new Contact({
firstName: data.fistName,
lastName: data.lastName
});
//Create the location document with the reference _id from the contact document
let location = await new Location({
city: data.city,
contact: contact._id
}).save();
//Push the location document in you Main document location array
main.locations.push(location);
main.save();
//return what ever you need
return main;
}
Query
let mainObj = await Main.findOne({name})
.populate({path: 'locations', populate: {path: 'contact'}})
.exec();
This approach is working for me, hope it serves you as well