How to populate nested fields in schema mongodb - arrays

I have a summary schema with a structure like this
{
sender: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
summary: {
type: String,
},
sent: {
type: Date,
default: Date.now,
},
}
);
then a convo schema:
NOTE: summary in this schema is an array of objectId, convo returns an array of objects after fetching, sender is in each of the objects gotten after populating the summary field
{
lastMessage: {
type: mongoose.Schema.Types.ObjectId,
ref: "messages",
},
members: {
type: [mongoose.Schema.Types.ObjectId],
ref: "User",
required: true,
},
summary: {
type: [mongoose.Schema.Types.ObjectId],
ref: "summary",
},
gigDetails: {
type: mongoose.Schema.Types.ObjectId,
ref: "Gig",
},
}
I want to populate the sender field in the summary array in the convo schema when fetching it, in addition to the already populated summary field.
How can I do this?

You can do it like this:
ConvoModel.find(filter).populate([{ path: 'summary', populate: { path: 'sender' } }])

// Fetch the convo documents
const convos = await Convo.find()
.populate("summary")
.exec();
// Iterate through the convos array
for (let i = 0; i < convos.length; i++) {
// Iterate through the summary array and populate the sender field in each element
const summaryPromises = convos[i].summary.map(async summary => {
return await summary.populate('sender').execPopulate();
});
// Wait for all promises to resolve
await Promise.all(summaryPromises);
}

Related

Why is my mongoose populate query throwing "Cannot populate path because it is not in your schema" error?

I'm building a form management program but right now I'm just trying to build a queue system to handle all the forms when they're assigned to someone.
when I call this first function, it should populate the elements of the activeWork array by pulling from each collection that the entries reference, there are several collections that could be referenced in active work, so I'm trying to use the collection type field to determine what collection to pull from, I don't know if I formatted any of this correctly because its my first time building any of this.
import statesPersons from "./statesPersons.schema.js";
export async function getStatesPersonsActiveWorkByProfileId(req, res){
try{
const { profileId } = req.params
const data = await statesPersons.find({profileId})
.populate('statesPersons.activeWork.referenceId')
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 })
}
}
Here is the schema for statesPersons, the collection where active work is stored.
import mongoose, {model, Schema} from "mongoose";
const activeWorkSchema = new Schema({
active: Boolean,
collectionType: {
type: String,
enum: ['messages'],
},
referenceId: {
type: Schema.Types.ObjectId,
refPath: "statesPersons.activeWork.collectionType"
},
sentBy: {
type: Schema.Types.String,
ref: "statesPerson",
},
sentTo: {
type: Schema.Types.String,
ref: "statesPerson",
},
timeRecived: Date,
dueDate: Date,
subject: String,
viewed: Boolean,
content: {},
})
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: [activeWorkSchema],
emailList: [String],
jobAssignments: [String],
affiantInfo: {
affiantInfoTitle: String,
affiantInfoExperience: String,
},
assessments: [
{
assessdBy: {
type: Schema.Types.ObjectId,
ref: "statesPerson",
},
dueDate: Date,
questions: {},
},
],
});
export default mongoose.model("statesPersons", statesPersonsSchema);
When I make a query, I get:
Cannot populate path statesPersons.activeWork.referenceId because it is not in your schema. Set the strictPopulate option to false to override.
I don't know if I formatted my populate correctly or if the problem is in my schema,

Mongoose query to find match data from objectId type field

Schema for Event
const eventSchema = new mongoose.Schema(
{
title: { type: String, required: true },
schedule: { type: Date, required: true },
organizer: { type: mongoose.Schema.Types.ObjectId, ref: "user", required: true },
},
{ timestamps: true }
);
Schema for Bookings
const bookingSchema = new mongoose.Schema(
{
requester: { type: mongoose.Schema.Types.ObjectId, ref: "user", required: true },
event: { type: mongoose.Schema.Types.ObjectId, ref: "event", required: true },
},
{ timestamps: true }
);
I want to find all the bookings by organizer id.
I tried the below query but it didn't work. Can anyone help me?
let bookings = await bookingModel.find({ "event.organizer": organizerId })
There are basically 3 options here:
denormalize the data and store the organizerId in the Bookings documents, so you can query it directly
use aggregation to read every Bookings documents, $lookup the corresponding Event documents, and then filter by the organizerId
use aggregation to $match Event documents with the organizerId, $lookup the corresponding Bookings documents, then $unwind and $project to return the just the Bookings documents.
db.bookings.aggregate([
{
$lookup: {
from: "events",
localField: "event",
foreignField: "_id",
as: "event"
}
},
{
$match : {"event.organizer": organizerId }
},
])
here is the solution How I solved this. Thank you all

How do I accept an array of strings using Apollo Server and GQL?

I currently have a typeDefs file that has a custom Books type. This is how it looks at the moment:
type: Books {
bookId: String
authors: [String]
description: String
title: String
}
I am using MongoDB in order to store my data. My model looks like this:
const bookSchema = new Schema({
authors: [
{
type: String,
},
],
description: {
type: String,
required: true,
},
// saved book id from GoogleBooks
bookId: {
type: String,
required: true,
},
title: {
type: String,
required: true,
}
});
And my resolvers look like this:
saveBook: async (parent, args, context) => {
if (context.user) {
const book = await Book.create({ ...args })
await User.findByIdAndUpdate(
{ _id: context.user._id },
{ $addToSet: { savedBooks: { bookId: args.bookId } } },
{ new: true }
);
return book;
}
throw new AuthenticationError('You need to be logged in!');
},
When I use graphql playground and send data in the query variable I am getting an error that String cannot represent a non string value: [\"james\", \"jameson\"]", when I send
{
"input": {
"bookId": "1",
"authors": ["james", "jameson"],
"description": "thdfkdaslkfdklsaf",
"title": "fdjsalkfj;a",
}
}
I know that it is because I am using an array of strings and entering an array of strings to gql will result in this error. I thought that if I put brackets around the String in my typeDefs it would work just find. I can't seem to find a way to send an array of strings to gql. I looked through the documentation and can't find a way to complete this..
Make a typedef out of author and then give the author variable within books the type "Author".
I think you also have to define the array in the bookschema if I'm not incorrect.
And don't forget to make sure the model naming in your database has to be the same as in your code.
Like this:
type: Author {
name: String
}
type: Books {
bookId: String
authors: [Author]
description: String
title: String
}
const bookSchema = new Schema({
authors: [
{
type: Author,
},
],
description: {
type: String,
required: true,
},
// saved book id from GoogleBooks
bookId: {
type: String,
required: true,
},
title: {
type: String,
required: true,
}
});
Hope this works :)

Mongoose: Find() return document with the the object that contains the value

I'm using MongoDB, and express.
I have a collection with all the countries, contains the states and cities.
Picture of a part of the collection
Now,
lets say I want to get all the cities that contains "Los" (like "San Antonio", "San Diego"),
when I'm using Find() - its return the all document with all the states and all the cities in the country (like in the picture above),
I want to return the all documents, but return only the objects that contains the value, inside array "cities"
Note: I expect to get different countries with cities that contains a part of the value in their names.
how to use the Find() to return as I want ?
hers is the schema I'm using:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CitiesSchema = new Schema({
capital:{
type: String,
required: true
},
currency:{
type: String,
required: true
},
emoji: {
type: String,
required: true
},
emojiU: {
type: String,
required: true
},
id: {
type: Number,
required: true
},
iso2: {
type: String,
required: true
},
iso3: {
type: String,
required: true
},
name: {
type: String,
required: true
},
native: {
type: String,
required: true
},
phone_code: {
type: String,
required: true
},
region: {
type: String,
required: true
},
states:
[{
cities:[{
id:{
type: Number,
required: true
},
latitude:{
type: String,
required: true
},
longitude:{
type: String,
required: true
},
name:{
type: String,
required: true
},
}],
id:{
type: Number,
required: true
},
name:{
type: String,
required: true
},
state_code:{
type: String,
required: true
},
}]
,
subregion: {
type: String,
required: true
}
});
const Cities = mongoose.model('Cities',CitiesSchema);
module.exports = Cities;
Edit: my Find() code:
Cities.find({"states.cities.name": city})
.then((data)=>{
return res.json(data);
})
lets say I want to search cities with the name "Los Santos". I want the result to be like this:
Picture of the result I expect to be return
instead, I'm getting the all states and cities in the countries - which I don't want.
UPDATE:
I found a way to return the data as I wish, by using aggregate with $unwind.
Cities.aggregate([{
$unwind: '$states'
},
{
$unwind: '$states.cities'
},
{
$match: {
'states.cities.name': {$regex: city, $options: "i"}
}
}
],
function (error, data) {
return res.json(data);
})
Try using RegEx, I use it while doing searches on MongoDB.
Cities.find({"states.cities.name": new RegExp(city)})
.then((data)=>{
return res.json(data);
})
I found a way to return the data as I wish, by using aggregate with $unwind.
Cities.aggregate([{
$unwind: '$states'
},
{
$unwind: '$states.cities'
},
{
$match: {
'states.cities.name': {$regex: city, $options: "i"}
}
}
],
function (error, data) {
return res.json(data);
})

Apollo GraphQl model attribute is not accessing object in ReactJs

I am implementing category and subcategory display in ReactJs using Apollo GraphQl Query.
I tried to using same table as category with fields.
id,
category_name,
category_img,
category_parent_id ( id from same table),
category_status,
typeDefs and resolver are belows
Category.js
const typeDefs = gql`
extend type Query {
getSingleCategory(id: ID): allCategory
}
`;
type allCategory {
id: ID!
category_name: String
category_img: String
category_parent_id: Int
category_status: Status
}
const resolvers = {
Query: {
getSingleCategory: async (parent, args, context, info) => {
var data = await db.category.findOne({
where: {
id: args.id,
},
include: [
{
model: db.category,
as: "children",
attributes: [["category_name", "children_name"]],
nested: true,
required: false,
},
],
required: false,
});
return data;
},
},
},
Model in GraphQl
module.exports = function (sequelize, DataTypes) {
var category = sequelize.define(
"category",
{
id: {
type: DataTypes.INTEGER(10).UNSIGNED,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
category_name: {
type: DataTypes.STRING(256),
allowNull: false,
},
category_img: {
type: DataTypes.STRING(256),
allowNull: false,
},
category_parent_id: {
type: DataTypes.INTEGER(10).UNSIGNED,
allowNull: false,
references: {
// WorkingDays hasMany Users n:n
model: "category",
key: "children",
},
},
category_status: {
type: DataTypes.ENUM("Acitve", "Inactive"),
allowNull: false,
},
},
{
tableName: "category",
timestamps: false,
}
);
category.associate = function (models) {
models.category.belongsTo(models.category, {
onDelete: "CASCADE",
foreignKey: "category_parent_id",
as: "children",
targetKey: "id",
});
};
return category;
};
In ReactJs
category.ts
export const GET_CATEGORYBY_ID = gql`
query($catId: ID!) {
getSingleCategory(id: $catId) {
id
category_name
category_img
category_parent_id
category_status
}
}
`;
I am trying to accessing {data.getSingleCategory} , I got all parameters but not able to get children_name from same table as parent_name.
Anyone can tell me what is the issue I am not able to access that children_name as attribute from same table Or there any other way so that we can access category/subcategory from same table and display it to reactjs template.
Not defined [separately] in types, not used/defined [as 'children' property?] in parent type, not requested in query ... simply filtered out from response.

Resources