Mongodb onetoone accessing related data - mongodb

Given the following schemas:
Schemas.Book = new simpleSchema({
"name": {
type: String
},
"publisher: {
type: Schemas.Publisher
}
});
Schemas.Publisher = new simpleSchema({
"name": {
type: String
}
});
If I do a find query on the Book schema, how can I return the one to one related Publisher please?
eg: db.book.find({}); currently returns:
{ "_id": ObjectId("objectid"), "name": "Book 1", "publisher": ObjectId("pubObjectId") }
I need to resolve the publisher.name. Hope that is clear enough.

Mongo doesn't provide JOIN operation, so you need to make it in your application. Something like:
var PublisherId = db.book.findOne({'name':'Book 1'})["publisher"]
var PublisherName= db.publisher.findOne({'_id': PublisherId })["name"]

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

Documents inserted without schema not being found with schema

I have two new collections in MongoDB of data that I pulled from an old Firestore database that I'm moving to mongo. Since the total number between these two collections is roughly 20,000, I opted to paste the raw JSON into the insert document section in mongo, which worked like a charm and I didn't have to write a new insert route to do the same.
I then created a schema in Mongoose that matched the inserted documents, and tried to use the schema to pull back some data, and its always returning nothing.
An example of a ticket inserted via JSON:
{
"title": "How to add and manage users for your company in QuickBooks Online",
"priority": "1",
"type": "Video",
"course": "G205",
"transcriptId": "07dom27Zz98jakvB1oh5",
"status": "In Review",
"tags": "",
"url": "",
"revisionNumber": 0,
"directoryId": 19,
"checkedOut": false
},
And my schema I made to match. The collection name in mongo is also called oldTickets, the plural of my schema name here:
const mongoose = require('mongoose');
var Schema = mongoose.Schema
const schema = new Schema({
course: { type: String },
title: { type: String },
priority: { type: String },
type: { type: String },
course: { type: String },
transcriptId: { type: String },
status: { type: String },
tags: { type: String },
url: { type: String },
revisionNumber: { type: Number },
directoryId: { type: Number },
checkedOut: { type: Boolean },
});
module.exports = mongoose.model('oldTicket', schema);
And finally my model import and fetch call:
const OldTicket = require('./models/model_old_ticket');
/***************************************************************************
* Get Old Tickets - Returns all old tickets, 10 at a time
****************************************************************************/
app.get('/getOldTickets/:offset', (req, res) => {
checkConnection();
OldTicket.find().skip(parseInt(req.params.offset)).limit(10).exec((err, data) => {
if (err){ res.status(500).send({err: err}); }
//If we got data, count the tickets & return the tickets & count
if (data) {
OldTicket.find().countDocuments().then(count => {
return res.status(200).send({
tickets: data,
count: count
})
})
}
});
});
Why isn't this finding anything? Both the count and the tickets are 0. I've run into this issue before when manually creating a collection without a schema, and in those instances I would simply delete the collection, write a route to create a document, and then things would work fine. But with the large data size of these two collections, I'd rather not do that since everything should be working as is.
Edit: Example of document in Mongo
And the name of the collection I'm currently viewing:
And I just now realized that for some reason there are now two collection names, oldTickets, which has data, and oldtickets, which is empty. I'm assuming my query is searching through the empty one? How can I get it to go to the one that actually has data?
can you attach the screenshot of your data with the collection? might be it's different.in mongoose, every collection name is complete with 's'. please verify your collection is created manually by you then it has to same as mongoose schema and also completed with 's'.
example:
const mongoose = require("mongoose");
const schema = new mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
index: true
},
filmId: {
type: mongoose.Schema.Types.ObjectId,
index: true
},
filmType: {
type: String,
index: true
},
birthday: {
type: Date
},
age: {
type: Number
},
terms: {
type: Boolean
}
},
{
versionKey: false,
timestamps: true,
}
);
schema.index({ filmId: 1, user: 1 })
module.exports = mongoose.model("UserAgeVerification", schema);
see my database

Prevent _id from being populated by just one schema

So I have the following schema:
var List = new Schema(
{
item_details_template: {
default: [
{
"title": "Name",
"content": "",
"hidden": false,
"order": 0,
"the_type": "text"
},
{
"title": "Price",
"content": "",
"hidden": false,
"order": 1,
"the_type": "text"
},
{
"title": "Company",
"content": "",
"hidden": false,
"order": 2,
"the_type": "text"
}
],
type: [Item_Detail]
}
}
)
However, I don't want ONLY this schema (subdocument) to not create _id fields. How do I do this? I know you can change the original schema itself, but it's being used by other Schemas, where I would like the _id to be populated.
To suppress _id on the item_detail_template, you need to restructure the way you create subdocument as follows
var mongoose = require("mongoose");
var subSchema = mongoose.Schema({
//your subschema content
},{ _id : false });
var schema = mongoose.Schema({
// schema content
subSchemaCollection : [subSchema] // item_details_template
});
var model = mongoose.model('tablename', schema);
If you want to suppress _id on the item_detail_template in the List schema only:
var List = new Schema(
{
item_details_template: {
_id:false, // should supress _id property
default: [
...
var widgetSchema = new Schema({ ... attributes ... }, { versionKey: false });

How to set property of a model from a property of a `population` field in mongoose?

I have a Post and its author property is set to User by using ref. Suppose there is a country field in User, how to make Post schema to have a country property as well which value is from User.country after population.
const Post = new Schema({
text: String,
author: {
type: ObjectId,
ref: 'User'
},
// How to set the virtual property country which is from User.country?
});
I know there is a Populate Virtuals in Mongoose, but it seems it just another way of populating, it won't pick up one of the properties, but the whole referenced record. Or maybe I get it wrong?
I know I can refer the country from Post.author.country, but I want a Post.country as well.
How to solve this?
Could I do this at the schema level?
If you use Populate Virtuals you can do the following :
var schemaOptions = {
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
};
var PostSchema = new Schema({
text: String,
id: false,
author: {
type: Schema.Types.ObjectId,
ref: 'User'
}
}, schemaOptions);
PostSchema.virtual('country').get(function() {
return this.author.country;
});
Using :
Post.find({}).populate('author').exec(function(error, data) {
console.log(JSON.stringify(data));
});
it gives :
[{
"_id": "59d924346be5702d16322a67",
"text": "some text",
"author": {
"_id": "59d91f1a06ecf429c8aae221",
"country": "France",
"__v": 0
},
"__v": 0,
"country": "France"
}]
Check this gist for a full example

How to correctly link collections in MongoDB/Mongoose

I am currently developing a Questionbank and I would like to link test results to user accounts. I am new to the noSQL database structure and I just wanted an opinion from the experts as to the best way to link the results to the user.
Should I have the userSchema to have a reference to a user specific test score collection? Or just link to keys in a global collection of test scores with "ref:"?
You should think data which your need as a JSON and other entities as a properties and property array.
I hope this code will help you:
Question:
{
"title" : "It's a title of question",
"otherProperty" : Boolean,
"created_by: {
"name": "User Who Created Question",
"otherProperty":0
},
"answers": [
{"text":"Answer content",
"created_by": {
"name":"User Who Answered Question
},
"otherProperty":False
},
{"text":"Answer content",
"created_by": {
"name":"User Who Answered Question
},
"otherProperty":False
},
{"text":"Answer content",
"created_by": {
"name":"User Who Answered Question
},
"otherProperty":False
}
]
}
In side of code you should read mongoose docs but you will use Ref key usually. Like this:
var Schema = mongoose.Schema;
var QuestionSchema = new Schema({
title: {
type: String,
required: true
},
answers: [{
type: Schema.Types.ObjectId,
ref: 'Answer'
}]
})
var Answer = new Schema({
text: {
type: String,
required: true
},
created_by: {
type: Schema.Types.ObjectId,
ref: 'User'
}
})
I hope it will help you.
UPDATE: I just updated sample code for first example.