So I have the following schemas:
var Item_Detail = new Schema(
{
content: {
index: true,
type: Array
},
is_private: {
default: false,
index: true,
type: Boolean
},
order: {
index: true,
required: true,
type: Number
},
table: {
default: {},
type: Object
},
title: {
required: true,
index: true,
type: String,
},
type: {
default: "text",
enum: ["text", "table"],
index: true,
type: String
},
},
{
strict: false
}
)
const Item = new Schema(
{
details: {
default: [],
index: true,
type: [Item_Detail],
},
display_name: {
default: "",
index: true,
type: String,
},
image: {
default: "http://via.placeholder.com/700x500/ffffff/000000/?text=No%20Image&",
type: String
},
is_private: {
default: false,
index: true,
type: Boolean
},
tags: {
index: true,
type: [Tag]
}
},
{
strict: false
}
)
Now, Item_Detail is to be a subdocument of Item, but I'm not quite sure how I should enforce the defaults and type restriction. I also don't want Item_Detail to be a collection in its own right, so using create or save probably doesn't fit.
I think you can use embedded documents for this so in your item schema you can embed an item_detail:
const Item = new Schema({
...
item_detail: item_detail
})
Then on the server when you want to add an item_detail you can do the following
myItem = new Item({//enter item data here})
//assign item detail here
myItem.item_detail = item_detail ;
then proceed to save it
myItem.save()
Enforcing the type is easy the default value is the tricky one but mongoose allows you to specify a function instead of boolean for default (just like it allows you the same for required).
So you could do something like this (I shortened the schemas for bravity):
const itemDetails = new Schema({
info: {
type: String,
required: true
}
})
const item = new Schema({
details: {
default: function() {
return [new ItemDetails({ info: 'N/A' })]
},
type: [itemDetails],
}
})
This would allow you to do something like this:
var itm = new Item();
And the saved result would be:
{
"_id": ObjectId("5b72795d4aa17339b0815b8b"),
"details": [{
"_id": ObjectId("5b72795d4aa17339b0815b8c"),
"info": "N/A"
}]
}
So this gives you two things:
You cannot put any type in details other than itemDetails since you enforced the type with itemDetails.
You initialize your ItemDetails object with defaults you want in your default custom function.
Hope this helps.
Related
I am using the mongoose updateMany() method and I also want to keep it a part of transaction. The documentation shows the example of save() where I can do something like Model.save({session: mySession}) but don't really know how to use it with for example Model.updateMany()
UPDATE:
For example I have two models called SubDomain and Service and they look like this respectively:
SUB-DOMAIN
{
name: {
type: String,
required: true,
},
url: {
type: String,
required: true,
unique: true,
},
services: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Service",
},
],
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
}
SERVICE:
{
name: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
price: { type: Number },
tags: { type: Array },
packages: [
{
name: { type: String, required: true },
description: { type: String, required: true },
price: { type: Number, required: true },
},
],
map: { type: String },
isHidden: {
type: Boolean,
required: true,
default: false,
},
sortingOrder: { type: Number },
isForDomain: { type: Boolean, required: false, default: false },
isForSubDomain: { type: Boolean, required: false, default: false },
subDomains: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "SubDomain",
},
],
}
Now the main field here is the services field in SubDomain and subDomains field in Service.
The complicated part😅:
Whenever the user wants to create new service, I want to $push that service's _id into the array of services of all the subDomains inside that new service
And for that, I am using the updateMany() like this:
const sess = await mongoose.startSession();
sess.startTransaction();
const newService = new Service({
_id: mongoose.Types.ObjectId(),
subDomains: req.body.subDomains
...foo
})
await SubDomain.updateMany(
{ _id: { $in: req.body.subDomains } },
{ $push: { services: newService._id } }
);
The problem starts here, of course I can do:
newService.save({session: sess})
but how do I keep my SubDomain's updateMany in the same transaction (i.e sess)
I know my example is difficult to wrap your head around but I have tried to pick a simplest example rather than copying the exact same code which would have been a lot more difficult
I have this code that is supposed to edit an object grabbed from a MongoDB. One of the fields is a map that has an array in it. I push to that array in the map. I log the object itself and it definitely got put into the array of the Map. It just doesn't update in the Database
Guild Model:
let {
Schema,
model
} = require('mongoose');
let Guild = new Schema({
guildID: {
type: String,
required: true
},
guildname: {
type: String,
required: true
},
prefix: {
type: String,
required: false,
default: "y!"
},
welcome_channel: {
id: {
type: String,
required: false,
default: null
},
message: {
type: String,
required: false,
default: "has joined the server!",
}
},
mod_log: {
type: String,
required: false,
default: null
},
spaces: {
type: Map,
required: false,
default: []
}
})
module.exports = model('Guild', Guild);
I grab the guild object in my code later on, it isn't null or undefined.
let fetch_guild = await Guild.findOne({"guildid": id});
// successfully grabs the object from the db
let temp_space = fetch_guild.spaces.get(args[0]) ? fetch_guild.spaces.get(args[0]) : null;
if(temp_space.admins.includes(message.mentions.users.first().id)) return message.reply("That person is already an admin!");
temp_space.admins.push(message.mentions.users.first().id);
console.log(fetch_guild.spaces)
Result of that console.log:
Map {
'test' => { name: 'test', admins: [] },
'test2' => { name: 'test2', admins: [] },
'test3' => { name: 'test3', admins: [ '500765481788112916' ] }
}
And I save it using:
await fetch_guild.save();
In the database, it is still an empty array
try using updateOne() of mongoose.
await fetch_guild.updateOne(fetch_guild);
I have some raw JSON that I have populated for testing purposes, but now I would like to put it into a mongoDB database using mongoDB Compass.
My mongoDB connection string is working and I have working mongoose code.
How do I go about doing this?
I would hope this would be an easy task as mongoDB stores it's data in the form of BSON already.
Here is a snippet of my code.
const json_string =
`[
{
"link": "https://www.youtube.com/watch?v=BMOjVYgYaG8",
"image": "https://i.imgur.com/Z0yVBpO.png",
"title": "Debunking the paelo diet with Christina Warinner",
// ... snip
},
{ // ... snip
The schema is already created:
// for relevant data from google profile
schema.Article = new Schema({
link: { type: String, required: true },
image: { type: String, required: true },
title: { type: String, required: true },
summary: { type: String, required: true },
tag: { type: String, required: true },
domain: { type: String, required: true },
date: { type: String, required: true },
timestamp: { type: Date, default: Date.now }
});
You can use this
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
mongoose.connect(process.env.MONGO_URI);
const articleSchema = new Schema({
link: { type: String, required: true },
image: { type: String, required: true },
title: { type: String, required: true },
summary: { type: String, required: true },
tag: { type: String, required: true },
domain: { type: String, required: true },
date: { type: String, required: true },
timestamp: { type: Date, default: Date.now }
});
const Article = mongoose.model("Article", articleSchema);
const json_string = `[
{
"link": "https://www.youtube.com/watch?v=BMOjVYgYaG8",
"image": "https://i.imgur.com/Z0yVBpO.png",
"title": "Debunking the paelo diet with Christina Warinner"
}
]`;
const jsonBody = JSON.parse(json_string);
for (let i = 0; i < jsonBody.length; i++) {
const data = jsonBody[i];
const article = new Article({
link: data.link,
image: data.image,
title: data.title
//.... rest
});
article.save();
}
Convert JSON string to an array
Loop through each object in the array
Create a new Article instance based on values from the object
Call the save method on the Article object
I'm trying to add a value to a nested schema:
groups = new SimpleSchema({
title: { type: String, optional: true },
element: { type: [elements], optional: true }
});
elements = new SimpleSchema({
description:{ type: String, optional: true },
anything: { type: String, optional: true }
});
MongoDB.attachSchema(new SimpleSchema({
title: { type: String },
slug: { type: String, unique: true },
language: { type: String, defaultValue: "en" },
group: { type: [groups], optional: true },
}));
Now I want to add just a new element-description to an existing entry in the DB. I tried this, but it doesn't work.
Uncaught Error: When the modifier option is true, validation object must have at least one operator
var newElement = {
description: 'insert this as a new element description'
};
MongoDB.update({ _id: Id }, { $push: { 'group.element': newElement }}, function(error) { if(error) console.warn(error); });
Is it correct to use 'group.element' as a $push-parameter?
Update
I forgot the index of group: $push: { 'group.0.element': newElement }
Also I have to define elements before groups in the schema.
I keep getting the error: Exception while invoking method 'createUser' Error: When the modifier option is true, validation object must have at least one operator when I try to create a user. I am using meteor-simple-schema, but none of the fixes with this error have worked for me. I have tried using blackbox and optional to see where the issue is but I keep getting the same error.
var Schemas = {};
Schemas.UserGamesPart = {
public: {
type: [String],
defaultValue: []
},
private: {
type: [String],
defaultValue: []
}
};
Schemas.UserGames = {
game1: {
type: Schemas.UserGamesPart
}
};
Schemas.UserProfile = new SimpleSchema({
games: {
type: Schemas.UserGames
}
});
Schemas.UpgradeDetails = new SimpleSchema({
subscribed_on: {
type: Date,
optional: true
},
stripe_charge_id: {
type: String,
optional: true
},
school_license: {
type: Boolean,
defaultValue: false,
optional: true
}
});
Schemas.UserProperties = new SimpleSchema({
paid: {
type: Boolean,
defaultValue: false
},
upgrade_details: {
type: Schemas.UpgradeDetails,
optional: true
}
});
Schemas.User = new SimpleSchema({
_id: {
type: String
},
username: {
type: String,
optional: true
},
emails: {
type: [Object]
},
"emails.$.address": {
type: String,
regEx: SimpleSchema.RegEx.Email,
optional: true
},
"emails.$.verified": {
type: Boolean,
optional: true
},
createdAt: {
type: Date
},
profile: {
type: Schemas.UserProfile,
blackbox: true,
optional: true
},
properties: {
type: Schemas.UserProperties,
blackbox: true,
optional: true
}
});
Meteor.users.attachSchema(Schemas.User);
My accounts.creaate user is as follows:
Accounts.createUser({
email: $('#email').val(),
password: $('#password').val(),
profile: {
games: {
game1: {
public: [],
private: []
}
}
}
});
Any ideas on how I can get this to work?
You forgot to add new SimpleSchema there in the beginning:
Schemas.UserGamesPart = new SimpleSchema({
public: {
type: [String],
defaultValue: []
},
private: {
type: [String],
defaultValue: []
}
});
Schemas.UserGames = new SimpleSchema({
game1: {
type: Schemas.UserGamesPart
}
});
Also I think your usage of the nested schemas is a little off. Only nest schemas when you need to reuse one. Creating a separate schema for UserGamesPart looks horrible. Try this instead:
Schemas.UserGames = new SimpleSchema({
game1: {
type: Object
}
'game1.public': {
type: [String],
defaultValue: []
},
'game1.private': {
type: [String],
defaultValue: []
}
});
This is shorter and easier to read.