Mongodb: Could the objects in array with $oid be populate? - mongodb

Has anyone ever used the objects in array with $oid and populate it?
Every class will have different is semester.So I use array to contain some detail of each semester,and have $oid for them.
boards:
{"_id":{"$oid":"63a07ecf6973176c890c8737"},
"classname":"math",
"uniqueData":[
{"semester":"2022-1","score":2,"_id":{"$oid":"63a07ec77777777777777777"}},
{"semester":"2022-2","score":2,"_id":{"$oid":"63a07ec88888888888888888"}}
],
}
reviews:
{
"uniqueId":{"$oid":"63a07ec77777777777777777"},
"score": 8
}
The review will point to certain class.semester. So I hope use populate to display the detail of that semester.
schemaReviews.find({}).populate({path: 'uniqueId'})
//I get:
{
"uniqueId":null,
"score": 8
}
//But I need:
{
"uniqueId":
{"semester":"2022-1","score":2,"_id":{"$oid":"63a07ec77777777777777777"}}
,
"score": 8
}
How do I got this done?
The models:
//
const schemaBoards = new mongoose.Schema({
classname:string
uniqueData:[{semester:string,score:int}],
})
mongoose.model('boards', schemaBoards)
//
const schemaReviews = new mongoose.Schema({
uniqueId: {
type: mongoose.ObjectId,
ref: 'boards'
},
score: Int
})
mongoose.model('reviews', schemaReviews)
Any Idea?

Related

GraphQL with Mongoose and MongoDB tips on deeply nested objects in arrays

I can't find any answers or tips on how to work with deeply nested data with GraphQL and Mongoose. I would've thought this is a fairly common thing to do, but the docs are also pretty vague when it comes to stuff like this.
Here's how my data should look like. It is basically and a collection of invoices. Each invoice data for that invoice like customer name, invoice number, etc. It also contains an array of sections. Each section has data about that section like the type of products, color of products, etc. Each section itself contains an array of invoiceLines, and they all contain a product that takes properties from the section it is contained in, and also has it's own data.
Pseudo code:
{
"invoices": [
{
"_id": "123456",
"invoiceNumber": "i2022-123",
"customer": "John Doe",
"date": "2022-11-02",
"sections": [
{
"type": "phones",
"color": "red",
"invoiceLines": [
{
"product": "iPhone",
"year": "2022"
},
{
"product": "Samsung",
"year": "2021"
}
]
},
{
"type": "tablets",
"color": "black",
"invoiceLines": [
{
"product": "iPad",
"year": "2022"
},
{
"product": "Samsung tablet",
"year": "2021"
}
]
}
]
},
{
"Another": "Invoice"
}
]
}
My GraphQl queries look like so:
const query = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
getInvoices: {
type: new GraphQLList(ProjectType),
resolve(parent, args) {
return Project.find();
}
},
getInvoice: {
type: ProjectType,
args: { id: { type: GraphQLID } },
resolve(parent, args) {
return Project.findById(args.id);
}
}
}
});
Question #1: How would I query a specific section or an invoice line? they all have MongoDB IDs, but for some reason I can't use that to query them.
const { Project } = require('../../models/Project');
const { SectionType, SectionInputType } = require('../TypeDefs/SectionType');
const ProjectType = require('../TypeDefs/ProjectType');
const mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
// Add a Project
addProject: {
type: ProjectType,
args: {
date: { type: GraphQLString },
invoiceNumber: { type: GraphQLNonNull(GraphQLString) },
customer: { type: GraphQLNonNull(GraphQLString) },
},
resolve(parent, args) {
const project = new Project({
date: args.date,
invoiceNumber: args.invoiceNumber,
customer: args.customer,
sections: [],
})
return project.save()
}
},
// Add a Section
addSection: {
type: SectionType,
args: {
// MongoDB ID for the project the section belongs to
id: { type: GraphQLID },
section: { type: SectionInputType }
},
async resolve(parent, args) {
const newSection = args.section;
return await Project.updateOne({ _id: args.id }, {
$push: { sections: newSection }
})
}
},
}
});
I'm using $push to add a section to the invoice.sections and that works perfectly because I can get a hold of the invoice by the MongoDB ID.
Question #2: In that case how would I be able to add invoice lines to these sections that I add with this method, since I'm not able to get a hold of the sections by their respective _id.
I guess my main issue is that I'm not able to get a hold of nested MongoDB IDs.
Any help would be appreciated, or any pointers to good resources for GraphQL and Mongoose.
P.S. Yes, I'm new to GraphQL, but I like the concept of it so I wanted to explore it.
I've tried resources from YouTube and from graphQL docs, but pretty much everything is vague when it comes to a problem like this. I would think the deeply nested data like this is a common occurrence, but I can't find proper resources

Comparing JSON array with Mongo array document

Currently I have a mongoose model 'Event' that stores a list of UUIDs as participants that reference another model 'User'
.
participants: [{
_id: false,
id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
tickets: {
type: Number,
min: 0,
},
}],
winners: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
.
.
Now I receive a request with the following JSON data to update my winners
{
"winners": [
"5f61132da98bac2a98487d79",
"5f611378a98bac2a98487d7a",
"5f611378a98bac2a98487d77"
]}
Is there a way to compare this array with participant field of that model and only allow entry of user ids that are participants? For example
const event = await Event.findOne({_id: _id}, 'participants.id -_id');
console.log(event.participants);
Output:
[
{ id: 5f61132da98bac2a98487d79 },
{ id: 5f611378a98bac2a98487d7a },
{ id: 5f6113b1a98bac2a98487d7b }
]
console.log(req.body.rewards);
[
'5f61132da98bac2a98487d79',
'5f611378a98bac2a98487d7a',
'5f611378a98bac2a98487d77'
]
Clearly the last UUID is not matching (is not a participant) so should be discarded and other two remaining should be updated in winners field. JSON object can be made flexible if needed.
Using aggregation framework does not really help me on this problem, and it might be very costly to project specific elements for a single document only. A work around solution would be using arrays.
const event = await Event.findOne({
_id: _eventId,
rewards: { $elemMatch: { _id: _rewardId }}
}, 'participants.id -_id');
const allParticipants = []
(event.participants).forEach((item) => {
allParticipants.push(item.id.toString());
});
const validIds = (req.body.winners).filter(item => allParticipants.includes(item));
This will check for arrays using includes and filter and return matching array item from the request and participants in the event.

How to project updated values only using findOneAndUpdate in embedded array Mongoose?

Currently my User model looks like:
{
_id: 'SomeId'
firstName: 'John',
lastName: 'Cena',
books: [
{
_id: 'xyz',
title: 'a',
author:'b',
ratings:[
{source:'source1', value:"8"},
{source:'source2', value:"9"}]
},
{
_id: 'abc',
title: 'c',
author:'d',
ratings:[
{source:'source3', value:"7"},
{source:'source4', value:"5"}]
}
]
}
After making an findOneAndUpdate query to update rating=>value of 1st book object(_id: "xyz") from 8 to 10 for a given source(say "source1"):
let up={
'books.$[book].ratings.$[rating].value':10
}
let filter={
new:true,
'books.rating':1, //I just want rating array of updated objects in it
arrayFilters:[
{ 'book._id':'xyz'},
{ 'rating.source': 'source1'}
]
}
User.findOneAndUpdate({'_id':'userId','books._id':'xyz'},up,filter).select('books.rating').exec((err,doc)=> {
if (err) throw err;
console.log(doc);
}
My code updates the books=>rating=>value correctly but I can't get that updated rating of that book.
This gives me rating of all books with both updated and non updated values in it. Looks like:-
{
books: [{ ratings:[{source:'source1', value:"10"},{source:'source2', value:"9"}] },
{ ratings:[{source:'source3', value:"7"},{source:'source4', value:"5"}] }]
}
I think the data of 2nd book shouldn't be there at all according to my code. I expect the follwing output:
{
books: [{ ratings:[{source:'source1', value:"10"}] }
}
Please help me to write findOneAndUpdate query correctly!
you can use array.find() like this:
const updatebookSource = (sourceId, userId, bookId) => {
User.findOneAndUpdate({ _id: userId, "books._id": bookId }, up, filter).exec(
(err, doc) => {
if (err) throw err;
let res = doc.books[0].ratings.find(rating => {
return rating.source === sourceId;
});
console.log(JSON.stringify(res, null, 1));
}
);
};
This returns the updated object. Let me know if it works.

How can I push to Array in Object from Mongoose?

This is my Schema in Model.
const applicantEvaluationSchema = new mongoose.Schema(
{
applicantIdx: Number,
application: {
comments: [
{
userIdx: Number,
comment: String,
createdAt: Date,
},
],
evaluations: [
{
userIdx: Number,
point: Number,
},
],
},
interview: {
comments: [
{
userIdx: Number,
comment: String,
createdAt: Date,
},
],
evaluations: [
{
userIdx: Number,
point: Number,
},
],
},
}
);
I wanna push comment in application.comments
I got the idea that clone the Array, push my comment and update it
but I think There's the better way to push the object.
How can I solve it?
You can use Model.update with the $push operator:
//Your model
ApplicantEvaluation.update(
{ /* selection criteria */ },
{
$push: {
'application.comments': /* new comment object */
}
}
);
With the $push operator, you supply a field to push to and the new object. If you have a nested field, use dot syntax to access the field. Since the comments array is nested inside application use 'application.comments'.

Updating an element in an array after limiting the fields to the client

Let's say I have a Player collection and when in the context of a Game, it limits their games array to only include that game.
Meteor.publish('gamePlayer', function (playerId, gameId) {
check(playerId, String);
check(gameId, String);
if (Roles.userIsInRole(this.userId, Roles.getAllRoles().fetch(), gameId)) {
return Players.find({
_id: playerId,
games: {
$elemMatch: {
id: gameId
}
}
}, {
fields: {
"games.$": 1
}
});
}
});
Now I get back the structure I am expecting to get back on the client.
// server
> Players.findOne({ _id: "123456" });
{
_id: "123456",
battletag: "corvid#1234",
games: [{
id: "5678",
name: "Starcraft II",
class: "Zerg",
ladder: 23
}, {
id: "1234",
name: "World of Warcraft",
class: "Shaman",
ladder: 123
}]
}
// client
> var params = Router.current().params;
> Meteor.subscribe('gamePlayer', params.gameId, params.playerId);
> Players.findOne();
{
_id: "123456",
battletag: "corvid#1234",
games: [{
id: "5678",
name: "Starcraft II",
class: "Zerg",
ladder: 23
}]
}
What I am confused about is how you update an array of objects when you have limited the results fed back in a reliable way. I want to change the limited field's class to Protoss, for example.
How do you update an array of objects safely on the client in meteor when the fields are limited?
There is no issue as long as you update the correct games.id.
Players.update(
{
_id: "123456",
"games.id": 5678
},
{
$set: {
"games.$.class" : "Protoss"
}
}
)
The client only simulate the update on minimongo while the real update done on server. Whatever changes on server will be propagated back to client.