Can anybody help me please?
I have two schemas and models (I add full version of code to be clearly):
const HouseSchema: Schema = new Schema(
{
name: {type: Schema.Types.String, required: true},
class: {type: KeyDisplayNameSchema},
levels: {type: Schema.Types.Number},
price: {type: Schema.Types.Number, required: true},
beginDate: {type: Schema.Types.String},
squarePrice: {type: Schema.Types.Number},
order: {type: Schema.Types.Number, default: 0},
parking: {type: Schema.Types.Boolean},
endDate: {type: Schema.Types.String},
visibleInCarousel: {type: Schema.Types.Boolean, default: true},
isDeleted: {type: Schema.Types.Boolean, default: false},
apartmentComplex: {
type: Schema.Types.ObjectId,
ref: 'ApartmentComplex'
},
images: {
type: imagesSchema,
default: () => {
return {};
}
},
publishedDate: {type: Schema.Types.String}
},
{
toJSON: {virtuals: true},
toObject: {virtuals: true}
}
);
HouseSchema.virtual('sections', {
ref: 'Section',
localField: '_id',
foreignField: 'house'
});
HouseSchema.virtual('flats', {
ref: 'Flat',
localField: '_id',
foreignField: 'house'
});
HouseSchema.virtual('layouts', {
ref: 'HouseLayout',
localField: '_id',
foreignField: 'house'
});
HouseSchema.virtual('levelLayouts', {
ref: 'LevelLayout',
localField: '_id',
foreignField: 'house'
});
HouseSchema.virtual('parkingComplex', {
ref: 'ParkingComplex',
localField: '_id',
foreignField: 'houses'
});
const HouseModel = mongoose.model<House>('House', HouseSchema);
export default HouseModel;
const ParkingComplexSchema: Schema = new Schema(
{
name: {type: Schema.Types.String, required: true},
address: {type: Schema.Types.String},
city: {type: KeyDisplayNameSchema, required: true},
district: {type: KeyDisplayNameSchema, required: true},
undergroundStation: {type: KeyDisplayNameSchema},
levels: {type: Schema.Types.Number, required: true},
beginDate: {type: Schema.Types.String, required: true},
endDate: {type: Schema.Types.String},
isDeleted: {type: Schema.Types.Boolean, default: false},
houses: [
{
type: Schema.Types.ObjectId,
ref: 'House',
default: () => {
return [];
}
}
],
developer: {
type: Schema.Types.ObjectId,
ref: 'Developer'
},
apartmentComplex: {
type: Schema.Types.ObjectId,
ref: 'ApartmentComplex'
}
},
{
toJSON: {virtuals: true},
toObject: {virtuals: true}
}
);
ParkingComplexSchema.virtual('parkingPlaces', {
ref: 'ParkingPlace',
localField: '_id',
foreignField: 'parkingComplexId'
});
const ParkingComplexModel = mongoose.model<ParkingComplex>('ParkingComplex', ParkingComplexSchema);
export default ParkingComplexModel;
These models have ref's to each other.
Then I want to get Parking Complex and populate field 'houses'
If I do this:
getParkingComplex: async (parent, {uuid}) => {
const data = await ParkingComplexModel.findById(uuid).exec()
console.log(data)
return data;
}
I get Parking Complex and field 'houses' has two objectId of House:
{
isDeleted: false,
houses: [ 60aaa827ec89de07e0fb8db1, 60aaa827ec89de07e0fb8db2 ],
_id: 60aaa827ec89de07e0fb8db3,
levels: 2,
beginDate: '2021-09-23T19:07:45.190Z',
endDate: '2023-05-23T19:07:45.194Z',
developer: 5ecd1c2590de761738c029a3,
apartmentComplex: 5ecd20b290de761738c029ad,
__v: 0,
id: '60aaa827ec89de07e0fb8db3'
}
But if I do this:
getParkingComplex: async (parent, {uuid}) => {
const data = await ParkingComplexModel.findById(uuid)
.populate('houses')
.exec()
console.log(data)
return data;
}
I get empty array of 'houses':
{
isDeleted: false,
houses: [],
_id: 60aaa827ec89de07e0fb8db3,
levels: 2,
beginDate: '2021-09-23T19:07:45.190Z',
endDate: '2023-05-23T19:07:45.194Z',
developer: 5ecd1c2590de761738c029a3,
apartmentComplex: 5ecd20b290de761738c029ad,
__v: 0,
id: '60aaa827ec89de07e0fb8db3'
}
What I do wrong? Thank for help!
Could you be...because you set the default return value for houses as an empty array?
...
houses: [
{
type: Schema.Types.ObjectId,
ref: 'House',
default: () => {
return [];
}
}
],
....
Related
I want to get comments of a post-blog sorted by top-like(rate)
Comment Model:
const CommentSchema = new Schema({
content: {type: String, maxLength: 100, required: true},
user: {type: Schema.Types.ObjectId, ref: 'User', required: true },
blog: {type: Schema.Types.ObjectId, ref: 'Blog', required: true},
reactions: [{type: Schema.Types.ObjectId, ref: 'Reaction'}],
},{ timestamps: true})
const ReactionSchema = new Schema({
user: {type: Schema.Types.ObjectId, ref: 'User', required: true},
comment: {type: Schema.Types.ObjectId, ref: 'Comment', required: true},
date: {type: Schema.Types.Date, default: Date.now, required: true}
})
my query:
this.model.Reaction.aggregate([
{
$group: {
_id: "$comment",
num: { $sum: 1 }
}
},
{ $sort: { num: -1 } },
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "_id",
as: "Com",
}
},
{ $unwind: "$Com" },
{
$match: {"Com.blog": '629af177e03ab4cfe845a9a4'}
},
{
$project: {
content: "$Com.content",
num: 1,
}
},
])
but $match after $lookup isn't accept! I try to use "$filter" also, but it get a "array " and a have a Object!
my query work without $match. but a need to filter comments base on blog-id
Can you try this,
this.model.Reaction.aggregate([
{
$group: {
_id: "$comment",
num: { $sum: 1 }
}
},
{ $sort: { num: -1 } },
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "_id",
as: "Com",
}
},
{ $unwind: "$Com" },
{
$match: {"Com.blog": mongoose.Types.ObjectId('629af177e03ab4cfe845a9a4')}
},
{
$project: {
content: "$Com.content",
num: 1,
}
},
])
Assuming such a mongoose database schema,
const teacherSchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
})
const courseSchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
teacher: { type: 'ObjectId', ref: 'Teacher', required: true },
})
const studentSchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
course: { type: 'ObjectId', ref: 'Course', required: true },
})
const hobbySchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
student: { type: 'ObjectId', ref: 'Student', required: true },
})
How can I query and return data from a specific teacher, while nesting courses they teach, students taking the course and their hobbies?
NOTE: I find teacher using url params (id)
{
_id: "61040a6dec6d054128fa8eae",
name: "John Doe",
others: ...,
courses: [
{
content: ...,
students: [
{
content: ....,
hobbies: [{
content:...
}
]
},
{...other students taking the course}
]
},
{...other subjects by John Doe}
]
}
All you need is mongo aggregate to link and obtain data from different schemas.
First, introduce a relationship linking all schemas, just like how you doing with the ref, I suggest have a
usersSchema and introduce a userType field that identifies the type of user either student or teacher, so you can do away with teacherSchema and studentSchema
UserSchema.aggregate([
{
$match: { id: teacherId }
},
{
$lookup: {
from: "users",
localField: "students",
foreignField: "_id",
as: "students",
},
},
{
$lookup: {
from: "hobbies",
localField: "hobbyId",
foreignField: "_id",
as: "hobbies",
},
},
{
$lookup: {
from: "course",
localField: "courseId",
foreignField: "_id",
as: "courses",
},
},
{
$sort: {
createdAt: -1,
},
},
{
$unwind: "$users",
},
])
.then((data) => {
return data
})
I have a Schema Events which has a key creator which is a reference to the creator and a key musicians which is an array with the musicians.
A Musician can create Events or participate as a musician. I want to create a virtual property named events to the musician which will contain all the events that the musician has created and the events that participates.
I tried this:
MusicianSchema.virtual('events', {
ref: 'Events',
localField: '_id',
foreignField: ['creator, musicians'],
justOne: false,
});
but this returns an empty array. Is there any way to make it work, or mongoose doesn't have this feature (yet)?
EDIT
This is the Event Schema:
const eventSchema = {
title: {
type: String,
trim: true,
required: true
},
description: {
type: String,
trim: true
},
startDate: {
type: Number,
required: true
},
endDate: {
type: Number,
required: true
},
picture: {
type: String,
get: getPicture,
},
location: {
type: [Number],
index: '2dsphere',
get: getLocation
},
creator: {
type: mongoose.Schema.Types.ObjectId,
required: true,
refPath: 'creatorType'
},
musicians: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Musician'
}],
creatorType: {
type: String,
required: true,
enum: ['Musician', 'Entity']
}
};
And the Musicians Schema:
const musiciansSchema = {
name: {
type: String,
trim: true,
required: true
},
picture: {
type: String,
get: getPicture,
},
description: String,
type: {
type: String,
required: true,
enum: ['Band', 'Artist'],
},
};
Mongoose virtual with multiple foreignField feature is not available at the moment, Suggestion for new feature has already requested on GitHub,
For now you need to use separate virtual/populate
MusicianSchema.virtual('creatorEvents', {
ref: 'Events',
localField: '_id',
foreignField: 'creator',
justOne: false,
});
MusicianSchema.virtual('musiciansEvents', {
ref: 'Events',
localField: '_id',
foreignField: 'musicians',
justOne: false,
});
I was facing similar situation during development and what I did was to replicate the same code twice but with different virtual names and it works perfectly.
In your case:
MusicianSchema.virtual('createdEvents', {
ref: 'Events',
localField: '_id',
foreignField: 'creator',
justOne: false,
});
MusicianSchema.virtual('musicianEvents', {
ref: 'Events',
localField: '_id',
foreignField: 'musician,
justOne: false,
});
I triying the following population/projection and work fine
modelBaby.findOne({'userId': req.user.data._id, '_id': req.params.id})
.populate('categories._categoryId', {
levels:{
$elemMatch:{
name: 'PURPLE'
}
}
})
But i need name as a variable something like:
modelBaby.findOne({'userId': req.user.data._id, '_id': req.params.id})
.populate('categories._categoryId', {
levels:{
$elemMatch:{
// over heree !!
name: 'categories._levelName' or this._levelName
}
}
})
the scheme ref is
const babyCategory = new mongoose.Schema(
{
answer: {type: String},
_categoryId: {type: Schema.Types.ObjectId, required: true, ref: 'category'},
_categoryName: {type: String, required: true},
_levelName: {type: String, required: true}
},
{versionKey: false, _id : false}
);
I've three mongoose models
When the user likes something it get saved in the Liked collection. But what I want to do is. Make a query that gives 10 available product the user hasn't liked before and is not his own product. I can't figure out how to make this possible in one query. Would it be possible?
User:
{
password: String,
type: {
type: String,
enum: ['user', 'admin'],
required: true,
default: "user"
},
firstName: {type: String},
middleName: {type: String},
lastName: {type: String},
gender: {type: String},
profilePicture: {type: Object},
setOwnProfilePicture: {type: Boolean, default: false},
facebook:{
id: {type: String},
token: {type: String}
}
}
Product:
{
condition: {
type: String,
enum: ['Als nieuw', 'Goed', 'Redelijk', 'Matig'],
required: true,
default: "Goed"
},
type: {
type: String,
enum: ['Ruilen', 'Doneren'],
required: true,
default: "Ruilen"
},
zipCode: {type: String},
houseNumber: {type: String},
distance: {type: Number},
likes: {type: Number, default: 0},
dislikes: {type: Number, default: 0},
title: {type: String},
description: {type: String},
owner: {type: Schema.ObjectId},
images: { type : Array , "default" : [] }
}
Liked:
{
ownerProductID: {type: Schema.ObjectId},
productID: {type: Schema.ObjectId},
liked: Boolean
}
UPDATE:
I find out what query to use, but in the result I have a field called liked but I want to filter this out, so I don't get any extra values in my result.
The query I use now:
db.products.aggregate(
// Pipeline
[
// Stage 1
{
$match: {'owner': {$ne: ObjectId("583f2f33ee975f4e8560b9fe")}}
},
// Stage 2
{
$lookup: {
"from" : "likes",
"localField" : "_id",
"foreignField" : "productID",
"as" : "liked"
}
},
// Stage 3
{
$match: {'liked.ownerProduct': {$nin: [ObjectId("585834609bb1aa1cbe98257b")]}}
},
// Stage 4
{
$limit: 10
},
]
);
Use aggregate to filter on multiple levels.
Assuming you have the USER_OBJECTID, first filter products on owner, then join the collection with Liked and filter again with user ID.
db.Product.aggregate([
{$match: {'owner': {$ne: '<USER_OBJECTID>'}}},
{$lookup: {
from: 'liked',
localField: '_id',
foreignField: 'productID',
as: 'likes'
}
},
{$match: {'likes.userID': {$nin: [<USER_OBJECTID>]}}},
{$limit: 10}
])
I managed to get the result I wanted. By using this query:
db.products.aggregate(
[
{
$match: {'owner': {$ne: ObjectId("583f2f33ee975f4e8560b9fe")}}
},
{
$lookup: {
"from" : "likes",
"localField" : "_id",
"foreignField" : "productID",
"as" : "liked"
}
},
{
$match: {'liked.ownerProduct': {$nin: [ObjectId("585834609bb1aa1cbe98257b")]}}
},
{
$limit: 10
},
{
$project: {
_id: 1,
owner: 1,
zipCode: 1,
title: 1,
description: 1,
houseNumber: 1,
images: 1,
dislikes: 1,
likes: 1,
type: 1,
condition: 1
}
},
]
);