MongoDB, Express + GraphQL server: Updated data model with new fields, but queries do not pick up new fields - mongodb

I created a GraphQL server in combination with Express + MongoDB. I started with the following data model:
const AuthorSchema = new Schema({
name: { type: String, required: true },
age: { type: Number, required: true },
});
Queries + Mutations are working, but I decided to add more fields to the data model:
const AuthorSchema = new Schema({
name: { type: String, required: true },
age: { type: Number, required: true },
bio: { type: String, required: true },
picture: { type: String, required: true }
});
I can add a new author through a mutation with the new fields, but for some reason, queries will not return the new fields.
{
"errors": [
{
"message": "Cannot query field \"bio\" on type \"Author\".",
"locations": [
{
"line": 3,
"column": 5
}
]
}
]
}```

Your GraphQL types are not the same as your Mongoose schemas. If you add a field to AuthorSchema schema and want to also expose this as a field on your Author type, then you need to explicitly define the field in your GraphQL schema.

Related

trying to populate a single field in each object inside an array of objects by dynamically picking the model to use with mongoose

I'm trying to use refPath to reference which collection to pull the population data from inside my schema, and even though it looks identical to the examples I've seen, its just not working.
Here is my schema for statesPersons, not super important, but it contains the activeWork array of objects.
import mongoose, {model, Schema} from "mongoose";
const statesPersonsSchema = new Schema(
{
profileId: {
type: String,
required: true,
unique: true,
},
department: {
type: String,
required: true,
index: true,
},
firstName: String,
lastName: String,
location: String,
org: String,
title: String,
jobDescription: String,
email: {
type: String,
lowercase: true,
},
phoneNumber: String,
activeWork: ["activeWork"],
emailList: [String],
jobAssignments: [String],
affiantInfo: {
affiantInfoTitle: String,
affiantInfoExperience: String,
},
assessments: [
{
assessdBy: {
type: Schema.Types.ObjectId,
ref: "statesPerson",
},
dueDate: Date,
questions: {},
},
],
},
{ strictPopulate: false }
);
export default mongoose.model("statesPersons", statesPersonsSchema);
Here is my schema for activeWork, the array of objects. This has the referenceId that I need to populate as well as the collectionType which I pull what collection it is from.
import mongoose, {model, Schema} from "mongoose";
const activeWorkSchema = new Schema(
{
active: Boolean,
collectionType: {
type: String,
enum: ["messages", "cases"],
},
referenceId: {
type: Schema.Types.ObjectId,
refPath: "collectionType",
},
sentBy: {
type: Schema.Types.String,
ref: "statesPersons",
},
sentTo: {
type: Schema.Types.String,
ref: "statesPersons",
},
timeRecived: Date,
dueDate: Date,
subject: String,
viewed: Boolean,
content: {},
},
{ strictPopulate: false }
);
export default mongoose.model("activeWork", activeWorkSchema);
And here is my query.
export async function getStatesPersonsActiveWorkByProfileId(req, res){
mongoose.set('debug', true);
try{
const { profileId } = req.params
const data = await statesPersons
.find({ profileId })
.populate('statesPersons.activeWork.referenceId')
.exec()
return res.send({
message: "success",
data: data,
status: 200 })
}catch(e) {
console.error(e.message)
return res.send({
message: "couldn't fetch active work",
data: null,
status: 500 })
}
}
its returning with the statesPersons object and the activeWork contains the objectId I need to populate, but its not populating. it looks like this.
"activeWork": [
{
"active": true,
"collectionType": "messages",
"referenceId": "63a49e3052658ce60c1dafcb",
"sentBy": "108416469928574003772",
"dueDate": "2018-02-21T11:16:50.362Z",
"subject": "testing",
"viewed": false,
"_id": "63a49e3052658ce60c1dafce"
I can force it to work by changing the query to be explicit.
const data = await statesPersons
.find({ profileId })
.populate({path: 'activeWork.referenceId', model: 'messages'})
.exec()
which looks like this.
activeWork": [
{
"active": true,
"collectionType": "messages",
"referenceId": {
"_id": "63a49e3052658ce60c1dafcb",
"involvedParties": [
"108416469928574003772",
"100335565301468600000"
],
"comments": [
{
"sender": [
"108416469928574003772"
],
"dateSent": "2022-12-22T18:13:04.604Z",
"content": "There is no way this is going to work.",
"_id": "63a49e3052658ce60c1dafcc"
}
],
But this wont work because I need it to be able to pull what model to use from the collectionType field
sorry for the late response , it seems like you are trying to populate the multilevel documents multilevel population.
here is an example.
db.statesPersonsSchema.find({ profileId }). populate({
path: 'activeWorkSchema',
populate: { path: 'referenceId' }
});

Query in command must target single shard key

I have an array of document ids using which I wish to delete the documents with the given id. The document id is also the shard key of the document. So I provided the following query for model.deleteMany(query)
query:
{ doc_id: { '$in': [ 'docid1', 'docid2' ] } }
I still get the error Query in command must target single shard key.
Is it possible to overcome this without looping through the array and deleting the docs one by one?
By matching any document in the collection with the _id field, you can make a query or delete command:
db.collection.deleteMany({ _id: { $exists: true }})
During the specification of the schema model in the code, a Shard Key (Partition Key) must be specified. We may execute operations such as save, update, and delete once it is provided
const mySchema = new Schema({
requestId: { type: String, required: true },
data: String,
documents: [{ docId: String, name: String, attachedBy: String }],
updatedBy: {
type: {
name: { type: String, required: true },
email: { type: String, required: true },
}, required: true
},
createdDate: { type: Date, required: true },
updatedDate: { type: Date },
}, { shardKey: { requestId: 1 } }
);

In MongoDb, are sub documents references or extensions

Using mongoose you can create a sub document as such:
export const UserSchema = new Schema({
email: {
type: String,
unique: true,
required: "email is required.",
match: [/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address']
},
firstName: {
type: String,
required: "firstName is required."
},
lastName: {
type: String,
required: "lastName is required."
},
dateOfBirth: {
type: Date,
required: "dateOfBirth is required."
},
roles: [{
role: String,
required: "role is required",
validate: isValidUserRole
}],
address: AddressSchema,
});
Address Schema
export const AddressSchema = new Schema({
streetAddress: {
type: String
},
addressLine2: {
type: String
},
city: {
type: String
},
state: {
type: String
},
zipCode: {
type: String
},
country: {
type: String
}
});
Whenever I save a user, will mongoose create a new user document with nested address data or a user and address and then reference that address document within the user instance?
How would I approach the case of 2 users sharing the same address?
Thanks!
You need not to provide the AddressSchema in the UserSchema schema.
You can just provide reference of AddressSchema like the following.
export const UserSchema = new Schema({
email: {
type: String,
unique: true,
required: "email is required.",
match: [/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address']
},
firstName: {
type: String,
required: "firstName is required."
},
lastName: {
type: String,
required: "lastName is required."
},
dateOfBirth: {
type: Date,
required: "dateOfBirth is required."
},
roles: [{
role: String,
required: "role is required",
validate: isValidUserRole
}],
address: {type:mongoose.Schema.Types.ObjectId,ref:'AddressSchema',required:true}
});
But remember that in order to make this work, 1st you need to create an Address data and provide their _id in UserSchema.
What happens by the above method is,
AddressSchema's ObjectId(_id) works as the REFERENCE in UserSchema. Basically, it acts as a REFERENCE DOCUMENT in UserSchema
You can find this out by querying your data through mongo shell.
When a document contains an embedded document (this is indistinguishable from nested hashes in the shell), the embedded document wholly belongs to the top level document and cannot be included-by-reference into another document.

How to construct a custom validator that makes a MongoDB field required if another field is a particular value?

I am using MongoDB and Mongoose.
Suppose I have the following Schema.
const notificationMetaSchema = new mongoose.Schema({
listingId: { type: mongoose.Schema.Types.ObjectId },
});
const notificationSchema = new mongoose.Schema({
category: { type: String, required: true, enum: [ "categoryA", "categoryB" ] },
meta: notificationMetaSchema,
});
I want my "listingId" field to be required only when the "category" field is "categoryA".
This validation ideally exists during both document creation and updates.
How do I construct a custom validator to achieve this effect?
EDIT
I have tried the following:
const notificationSchema = new mongoose.Schema({
category: { type: String, required: true, enum: [ "categoryA", "categoryB" ] },
meta: {
listingId: {
type: mongoose.Schema.Types.ObjectId,
required: function () {
return [
"categoryA",
].includes(this.category);
}
},
},
});
However, when I call the following query:
Notification.findOneAndUpdate({}, $set: { category: "categoryA", meta: {} }).exec();
No validation error is thrown
You can write a javaScript function for a field in mongoose schema, that function can act as custom validator, Your schema should look like :
const notificationSchema = new mongoose.Schema({
category: {
type: String,
required: true,
enum: ["categoryA", "categoryB"]
},
meta: {
listingId: {
type: mongoose.Schema.Types.ObjectId,
required: function checkRequiredOrNot() {
/** This function returns true or false, 'this.category' will retrieve current object's 'category' value */
return this.category == "categoryA" ? true : false;
}
}
}
});

How to give iDs to dynamic fields in React-Redux?

I created a simple dynamic fields in React-Redux with a plus button to add as many field as I want (hobbies) of an already existing form. I'm using mongodb as a database and so I have this error that tells me that my fields/data don't have iDs.
so how can I generate iDs for my data?
this below is my model with featherJs. as you can see this is how I added my hobbies array in the existing model called myService. I can see that my hobbies are created in mongo (using Robo 3T) which is great but i'm having difficulty reusing them (hobbies) in an other component in Redux. I'm not sure if I should give IDs to this fields or create a new service just for them. I never coded something in backend so I'm confused. what's the rule for this kind of situations.
Any other suggestions would be helpful.
warning in Redux: Each child in a list should have a unique "key" prop.
error in api : Cast to ObjectId failed for value at path "_id" for model "
const { Schema } = mongooseClient;
const myService = new Schema({
type: { type: String, enum: VALID_TYPES, required: true },
user: {
type: mongooseClient.Schema.Types.ObjectId,
ref: 'user',
required: true
},
comment: String,
hobbies: [{
type: mongooseClient.Schema.Types.ObjectId,
ref: 'hobbies',
default: [],
required: false }],
date: {
begin: { type: Date, default: Date.now },
current: { type: Date, default: Date.now },
end: { type: Date, required: true },
},
}, {
timestamps: true
});
return mongooseClient.model('myService', myService);
};