Refs mongo dont working nestjs with graphql - mongodb

I believe I am missing something I already tried to add populate but it doesn't work either I don't know what it is and I can't find any examples about relationships
I want to select many items for one user.
In items it references user but in user items[] returns blank and array is empty in mongo
Users Module
imports: [MongooseModule.forFeature([{ name: 'User', schema: UserSchema }])],
UserSchema
export const UsersSchema = new mongoose.Schema({
[...]
items: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Items'}],
});
Items module
imports: [
MongooseModule.forFeature([{ name: 'Item', schema: ItemSchema }]),
],
ItemSchema
export const ItemSchema = new mongoose.Schema({
title: String,
price: Number,
description: String,
user: { type: mongoose.Schema.Types.ObjectId, ref: 'Users'},
});
Mutation to create item in items.resolver.ts
#Mutation(() => ItemType)
async createItem(#Args('input') input: ItemInput): Promise<ItemInput> {
return this.itemsService.create(input);
}
and service with create method
async create(createItemDto: ItemInput): Promise<ItemType> {
const createdItem = new this.itemModel(createItemDto);
return await createdItem.save();
}
Item reference in users only shows an empty array in mongo, but my user reference in items shows object id as below

Related

How to use populate, within select, in MongoDB?

Suppose I have two schemas on MongoDB:
const personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
email: String,
things: [{ type: Schema.Types.ObjectId, ref: 'Thing' }]
});
const thingSchema = Schema({
_id: Schema.Types.ObjectId,
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
Every time a user logs in, I would like to show the things that they have posted, as well as the fans that are following each of the things. I am able to use populate and select to get to that:
const user = await personModel
.findOne({ _id: req.user._id })
.populate({
path: "things",
select: ["title", "fans"]
}),
However, I am only getting the id of each fan, and not the fan's name and email. I can't quite figure out how to use populate to reference the person collection again.
The outcome I am trying to achieve is that:
the user object would have an array of things
the thing object would have an array of fans
the fan object would have two values - name and email of the fan
You can do nested population with:
const user = await personModel
.findOne({ _id: req.user._id })
.populate({
path: 'things',
select: ['title', 'fans'],
populate: { path: 'fans' },
})
.exec();

The most efficient way to handle one to many relationship in MongoDB

Let's say I have a one-to-many relationship - A user has many todos. I want to be able to perform the following:
Give me a specific user without the todos
Give me the todos belongs to a specific user
Give me a single todo by id
Add, update, and delete todo
I can tackle it in three ways:
Embedding the todos inside the user document
import { Schema, model } from 'mongoose';
const userSchema = model('User', new Schema({
name: String,
todos: [{ title: String, id: String }],
}));
Use Todo Ref
import { Schema, model } from 'mongoose';
const userSchema = model('User', new Schema({
name: String,
todos: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Todo"
}],
}));
const todoSchema = model('Todo', new Schema({
name: String
}));
Use Todo Id
import { Schema, model, ObjectId } from 'mongoose';
const userSchema = model('User', new Schema({
name: String
}));
const todoSchema = model('Todo', new Schema({
name: String,
userId: ObjectId
}));
What will be the most efficient way to handle this scenario in MongoDB? Adding an index on the userId property in the last solution will make the query faster?

Populate an array in Mongoose

I am building a search query for training sessions that will return me return details of a session, populating data from the coach (ObjectId) and the participants (Array of ObjectIds). I can populate the coach but I can not populate the participants. My session schema is:
const mongoose = require('mongoose');
import { timestamp } from "./plugins/timestamp"
import { User } from './index'
const SessionSchema = new mongoose.Schema({
coach: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
title: { type: String, required: true, default: "Lacrosse Training Session" },
participants: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }]
});
SessionSchema.plugin(timestamp);
export const Session = mongoose.model('Session', SessionSchema);
And I am trying to populate with:
const session = await Session.findById(req.params.id).populate('coach').populate('participants');
Output
When I use only populate('coach'), I get something like:
coach: {address: {city: "Joes"}, name: "John John", …} <= <= <= POPULATED
participants: ["5ea43590f105a4188358210f", "5ea43590f105a4188358210e", "5ea43590f105a41883582115"]
But when I use populate('coach').populate('participants'), I get the same coach, but empty participants (participants: [])
Why is that? How can I populate each element of the participants array?
Thank you
you can use one of the following:
1-
const session = await Session.findById(req.params.id).populate([{ path: 'coach' }, { path: 'participants' }])
2-
const session = await Session.findById(req.params.id).populate({ path: 'coach' }).populate({ path: 'participants' });
also make sure that these participants Ids are already exist in the User collection

Mongoose: reference between models with same schema keys

As a front-end developer, I would like to have some isomorphic object for two mongoose models.
Let's say I have a user profile:
const profileSchema = new Schema({
firstName: { type: String },
lastName: { type: String },
// example of difference between model schemas
__user: { type: ObjectId, ref: 'Users' },
}
And I would like to create a list of Contact, where each contact will have some of the same keys:
const contactSchema = new Schema({
firstName: { type: String },
lastName: { type: String },
__profile: {
type: ObjectId,
ref: 'Profiles',
unique: true,
},
comment: { type: String },
}
NOTE: Contact could be the both:
as a reference to the Profile
and as independent record in DB / document.
==============================
My question: which is the best way to, organize models on such a way, so
contact could be a reference to the profile
when similar Profile key, like firstName will be updated, contact firstName will be updated too
AVOID of next ref
await Contact.findById(SOME_ID).populate('__profile');
// result
{
firstName: '',
lastName: '',
__profile: {
firstName: 'Chuck',
lastName: 'Norris',
}
}
Desired result - keep contact "isomorphic" like:
{
firstName: 'Chuck', // the key value from profile
lastName: 'Norris', // the key value from profile
__profile: SOME_PROFILE_ID,
}
Is this possible?
P.S: in my app, I'm using refs and started to use discriminators approaches.
I'd approach this task either:
1) put all data inside 1 collection (e.g. Profile):
// Profile model
{
firstName: 'Chuck',
lastName: 'Norris',
contacts: [{
type: ObjectId,
ref: 'Profile',
unique: true,
}],
...all other properties
}
that way you will be able to store just contacts (e.g. when I want to add just a contact) and profiles with much more info.
2) or will use discriminators to create a base class (e.g. Contact) and build Profile model upon it:
const options = { discriminatorKey: 'userType' };
const Contact = mongoose.model('Contact', new mongoose.Schema({
firstName: String,
lastName: String
},
options)
)
const Profile = Contact.discriminator(
'Profile',
new mongoose.Schema(
{
contacts: [{
type: ObjectId,
ref: 'Contact',
unique: true,
}],
comments: []
},
options
)
);
that way you will be able to save Contacts and Profiles inside 1 collection and reference base class (Contact) for contacts inside Profile
Hope that helps!
In my case, complete usage of Mongoose discriminators did not give me an advantage, because discriminators give you an opportunity to:
They enable you to have multiple models with overlapping schemas on
top of the same underlying MongoDB collection.
As a result, by using discriminators approach, I will reive one collection
of:
profiles
And there will be a mix of users and contact profiles.
==============================
So I decided to use two approaches:
create BesaSchema for the profile
take advantage of Mongoose Subdocuments
RESULT:
// keys which are same for both user Profile and Contact
const Schema = require('mongoose').Schema;
const util = require('util');
function BaseProfileSchema(...args) {
Schema.apply(this, args);
this.add({
firstName: { type: String },
lastName: { type: String },
});
}
util.inherits(BaseProfileSchema, Schema);
// user Profile Model
const profileSchema = new BaseProfileSchema({
__user: {
type: String,
ref: 'users',
required: true,
unique: true,
},
});
const Profile = mongoose.model('profiles', profileSchema);
// Contact with profile as subdocument
const contactProfileSchema = new BaseProfileSchema();
const contactSchema = new Schema({
// Associations
__refProfile: {
type: Schema.Types.ObjectId,
ref: 'profiles',
index: {
unique: true,
sparse: true,
},
},
profile: contactProfileSchema,
});
const Contact = mongoose.model('contacts', contactSchema);
As a result, I'm having DB with next collections:
users
profiles
contacts
Both profiles and contacts.profile are IDENTICAL because I'm extending the base shared schema.
Moreover:
inside Contact I'm having different keys for real referenced profile (__refProfile which can NOT be edit by others) and contact.profile
profile inside connection can be edited ONLY when the contact was edited by itself
P.S: happy codding 👨‍💻🍻

MissingSchemaError while using Mongoose Populate with only one model

**I have answered below. In short you need to require the Model in the module in which you wish to populate, even though you do not refer to it directly.
I am hitting a strange problem with mongoose when populating just one particular array of IDs.
I have three models, User, Company and Widgets.
When I return the company populated with the users all is fine using:
Company.findOne({ name: 'xyz' })
.populate('users')
.exec(function(err, company) {
if (err) return res.send(err)
res.send(company)
})
However when I try to replace populate 'users' with 'widgets' I get the following error:
{
"message": "Schema hasn't been registered for model \"widget\".\nUse mongoose.model(name, schema)",
"name": "MissingSchemaError"
}
Here are the models:
USER:
const UserSchema = new Schema({
name: String,
email: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
company: {
type: Schema.Types.ObjectId,
ref: 'company'
}
});
const User = mongoose.model("user", UserSchema);
COMPANY:
const CompanySchema = new Schema({
name: String,
URL: {
type: String,
unique: true
},
users: [{
type: Schema.Types.ObjectId,
ref: 'user'
}],
widgets: [{
type: Schema.Types.ObjectId,
ref: 'widget'
}]
});
const Company = mongoose.model('company', CompanySchema);
WIDGET:
const WidgetSchema = new Schema({
title: {
type: String,
required: true
},
maker: String
});
const Widget = mongoose.model('widget', WidgetSchema);
I have manually inspected the _ids in the widget array of the company model and they are all correct.
OK, so this was a lack of understanding on my behalf.
In the module where I was using:
Company.findOne({ name: 'xyz' })
.populate('users')
.exec(function(err, company) {
if (err) return res.send(err)
res.send(company)
})
I had imported the User model for other uses in the module. However, as I was not directly referring to Widget I had not imported it. Having done some more research I found that you need to import a model when populating even though not referring to it directly.
Let me know if best to delete whole thread or leave for reference.