I have an object:
{ SKU: 'TR1234',
Description: 'Item 1',
UoM: 'each',
client_id: '531382e3005fe0c926bd3957',
Meta: { Test: 'test1', Image: 'http://www.aol.com' } }
I'm trying to save it given my schema:
var ItemSchema = new Schema({
sku: {
type: String,
trim: true,
},
description: {
type: String,
trim: true,
},
company_id: {
type: Schema.ObjectId,
ref: 'Client',
},
createdOn: {
type: Date,
default: Date.now
},
updatedOn: {
type: Date,
default: Date.now
}
}, {versionKey: false});
But it doesn't save and I assume it's because of the capitalized key names. However, those are dynamically generated from a CSV which is parsed with https://github.com/Keyang/node-csvtojson
Ideas?
You can also just use a setter in your mongoose schema, like that:
function toLower (v) {
return v.toLowerCase();
}
var UserSchema = new Schema({
email: { type: String, set: toLower }
});
Just apply it to your fields.
There is also one more approach, just:
email : { type: String, lowercase: true }
Update for keys:
If you would like to change keys, you should the approach likes 'ecdeveloper' mentioned below. My answer was for values, so it makes sense to give this reputation to 'ecdeveloper'. Sorry for confusing.
Here is one more approach without creating a new object:
Object.prototype.keysToUpper = function () {
var k;
for (k in this) {
if (this.hasOwnProperty(k))
this[k.toLowerCase()] = this[k];
delete this[k];
}
return this;
};
What about calling toLowerCase() on each key from your object, and build a new object with lower case keys?
// Assumy your object name is obj
var newObj = {};
Object.keys(obj).forEach(function(key) {
newObj[key.toLowerCase()] = obj[key];
});
// Here you can save your newObj
Related
I have a simple schema as
const xpReward = new mongoose.Schema({
...,
receivedFor: {
type: Object,
required: true
}
});
Received for is an object which can have 2 keys and the values can be something like { "articleId": 5} or { "testId": 7}
I want to expand this receivedFor object with an required field, but keeping the possibility to add articleId or testId.
I know I can let it as in example above, because is an object and can have any form, but I want to specify the type there, to can be known in further usage.
I'm thinking at something like:
const xpReward = new mongoose.Schema({
...,
receivedFor: {
learningPath: {
type: String,
required: true
},
articleId | testId: {
type: Number,
required: true
}
}
});
I don't want to use another nested object as
receivedFor: {
learningPath: {
type: String,
required: true
},
// in this example, the old receivedFor will become this valueFor
// this will be just another nested level, one more parent for it
valueFor: {
type: Object,
required: true
}
}
Can be this done somehow better? thx
Would likely need some hooks to go along with this, but this is the basic idea.
const xpReward = new mongoose.Schema({
...,
receivedFor: {
learningPath: {
type: String,
required: true
},
foreignId: {
type: Number,
required: true
},
foreignCollection: { // maybe even make it an enum - articleId | testId
type: String,
required: true
}
}
});
I am new to Mongoose and would like to know if it is possible to add validators on the fly on some parameters depending on queries. I have for example a schema like below:
var user = new Schema({
name: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
city: { type: String },
country: { type: String }
});
For a simple registration i force users giving the name, the email and the password. The Schema on top is OK. Now later I would like to force users giving the city and the country. Is it possible for example to update a user's document with the parameters city and country on required? I am avoiding to duplicate user schema like below:
var userUpdate = new Schema({
name: { type: String },
email: { type: String },
password: { type: String },
city: { type: String, required: true },
country: { type: String, required: true }
});
What you would need to do in this case is have one Schema and make your required a function which allows null and String:
var user = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
city: {
type: String,
required: function() {
return typeof this.city === 'undefined' || (this.city != null && typeof this.city != 'string')
}
}
});
You can extract this and make it an outside function which then you can use for county etc.
What this does is it makes the field required but also you can set null to it. In this way you can have it null in the beginning and then set it later on.
Here is the doc on required.
As far as I know, no, it is not possible.
Mongoose schema are set on collection, not on document.
you could have 2 mongoose model pointing to the same collection with different Schema, but it would effectively require to have duplicated Schema.
personnally, in your situation, I would create a single home-made schema like data structure and a function who, when feeded with the data structure, create the two version of the Schema.
by example :
const schemaStruct = {
base : {
name: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
city: { type: String },
country: { type: String }
}
addRequired : ["city", "country"]
}
function SchemaCreator(schemaStruct) {
const user = new Schema(schemaStruct.base)
const schemaCopy = Object.assign({}, schemaStruct.base)
schemaStruct.addRequired.forEach(key => {
schemaCopy[key].required = true;
})
const updateUser = new Schema(schemaCopy);
return [user, updateUser];
}
I have a user model schema, a work model schema, and a critique model schema. The relationship between these schema's is a user can submit many works (like blog posts), and can comment/review (which we call critiques) other people's posts (works).
So when a user submits a critique (think of it like a review), this is my post route. I find the work by the id, then create a new critique model object, and pass that to the .create() mongoose function. All goes seemingly well until I hit the foundWork.critiques.push(createdCritique) line. the console log errors out saying:
BulkWriteError: E11000 duplicate key error collection: zapper.critiques index: username_1 dup key: { : null }
Obviously, it is saying that there are two username keys in the objects and they're conflicting with each other, but I'm not familiar enough with this to find the root of the issue and fix it in the mongoose models. The models are below. If anyone could help, that'd be greatly appreciated.
// post route for getting the review
router.post('/:id', isLoggedIn, function(req, res) {
Work.findById(req.params.id, function(err, foundWork) {
if (err) {
console.log(err);
} else {
// create a new critique
var newCritique = new Critique ({
reviewerName: {
id: req.user._id,
username: req.user.username
},
work: {
id: foundWork._id,
title: foundWork.title
},
critique : req.body.critique,
date: Date.now(),
rating: 0
});
// save new critique to db
Critique.create(newCritique, function(err, createdCritique) {
if (err) {
console.log(err)
} else {
console.log("Created critique is ");
console.log(createdCritique);
// push the new critique into array of critiques of the work
foundWork.critiques.push(createdCritique);
// save to db
foundWork.save();
}
});
}
});
User model:
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var UserSchema = new mongoose.Schema({
firstname: String,
lastname: String,
username: String,
password: String,
email: String,
zip: String,
bio: {
type: String,
default: ''
},
influences: {
type: String,
default: ''
},
favBooks: {
type: String,
default: ''
},
notWriting: {
type: String,
default: ''
},
favHero: {
type: String,
default: ''
},
favVillain: {
type: String,
default: ''
},
works: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Work'
}
],
critiques: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Critique'
}
],
friends: [
{
friendId: String,
friendName : String,
friendPic: String
}
],
friendRequests: [
{
sendingFriendId: String,
sendingFriendName : String,
sendingFriendPic: String
}
],
createdDate: {
type: Date,
default: Date.now
},
lastLogin: {
type: Date,
default: Date.now
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
Work model:
var mongoose = require('mongoose');
var WorkSchema = new mongoose.Schema({
title: String,
genre: String,
workType: String,
length: Number,
ageRange: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
manuscriptText: String,
critiques: [
{
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Critique"
}
}
],
ratingNumber: [Number],
ratingSum: {
type: Number,
default: 0
},
date: {
type: Date,
default: Date.now
},
isPublic: {
type: Boolean,
default: true
}
});
module.exports = mongoose.model("Work", WorkSchema);
Critique model:
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var CritiqueSchema = new mongoose.Schema({
reviewerName: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
work: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Work"
},
title: String
},
critique: String,
date: {
type: Date,
default: Date.now
},
rating: [Number]
});
CritiqueSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("Critique", CritiqueSchema);
When you create a unique index in MongoDB, the default behavior is that it will index null values also.
This means if you have a document in your collection with a username of null, you can not add another one with a username of null.
What you need is a sparse index which only indexes actual values (and ignores documents with null for that field).
Check this link It shows how to create a sparse index vs "normal" one in mongoose (index: true, vs spare: true). Most of the time you would want sparse indexes.
I've got the following data structure:
const schema = new Schema({
userId: { type: String, required: true },
image: { type: String, required: true},
name: { type: String, required: true },
description: { type: String, required: true },
subCategories: [{ type: mongoose.Schema.Types.ObjectId, ref: 'SubCategories' }]
}, {
strict: 'throw'
});
export const Plan = mongoose.model('Plan', schema);
const schema = new Schema({
userId: { type: String, required: true },
name: { type: String, required: true },
description: { type: String, required: true }
}, {
strict: 'throw'
});
export const SubCategories = mongoose.model('SubCategories', schema);
and I want to save each of the subCategories in a separate collection each time a new Plan entity is created.
This requires me to iterate the subCategories and to create each one of the sub-category.
for example:
plan.subCategories = await Promise.all(plan.subCategories.map(async (s: IWorkoutDay) => {
const sub = new SubCategories(s);
sub.userId = userId;
await sub.save();
return sub;
}));
const p = new Plan(plan);
p.userId = userId;
await p.save();
and also each time I want to edit the subCategories of the element I'm required to iterate find the element and to update each one separately.
is there an easier way to achieve this? since it seems pretty complicated to me.
I can't save an array of strings into my DB using Mongoose.
(Note all code below is simplified for ease of writing here)
So i declare a variable of a person schema I have:
var newPerson = new Person ({
tags: req.body.tags
});
The schema itself looks like:
var personSchema = new mongoose.Schema({
tags: Array
});
And when it comes to saving its just a simple:
newPerson.save(function(err) {
//basic return of json
});
So using Postman I send in an array in the body - however everytime I check the DB, it just shows one entry with the array as a whole i.e. how I sent it:
Any ideas what extra I'm supposed to do?
Write up from my comment:
The way to specify an array of strings in mongoose is like so:
var personSchema = new mongoose.Schema({
tags: [{
type: String
}]
However, the problem here is most-likely to do with Postman as it is sending the 'array' as a string. You can check this by checking the type of req.body.tags like so:
console.log(typeof req.body.tags)
If this returns a String, make sure to set the content-type in Postman to JSON as seen in this screenshot rather than the default 'form-data' option.
var schema = new Schema({
name: String,
binary: Buffer,
living: Boolean,
updated: { type: Date, default: Date.now },
age: { type: Number, min: 18, max: 65 },
mixed: Schema.Types.Mixed,
_someId: Schema.Types.ObjectId,
decimal: Schema.Types.Decimal128,
array: [],
ofString: [String],
ofNumber: [Number],
ofDates: [Date],
ofBuffer: [Buffer],
ofBoolean: [Boolean],
ofMixed: [Schema.Types.Mixed],
ofObjectId: [Schema.Types.ObjectId],
ofArrays: [[]],
ofArrayOfNumbers: [[Number]],
nested: {
stuff: { type: String, lowercase: true, trim: true }
},
map: Map,
mapOfString: {
type: Map,
of: String
}
})
// example use
var Thing = mongoose.model('Thing', schema);
var m = new Thing;
m.name = 'Statue of Liberty';
m.age = 125;
m.updated = new Date;
m.binary = Buffer.alloc(0);
m.living = false;
m.mixed = { any: { thing: 'i want' } };
m.markModified('mixed');
m._someId = new mongoose.Types.ObjectId;
m.array.push(1);
m.ofString.push("strings!");
m.ofNumber.unshift(1,2,3,4);
m.ofDates.addToSet(new Date);
m.ofBuffer.pop();
m.ofMixed = [1, [], 'three', { four: 5 }];
m.nested.stuff = 'good';
m.map = new Map([['key', 'value']]);
m.save(callback);
On Schema:
( Since you have mentioned in the problem that it is an array of strings )
var personSchema = new mongoose.Schema({
tags:{
type:[String],
required: true
}
});
On Postman:
{
"tags": ["css", "javascript", "mongoose", "node"]
}
On MongoDB
{
"tags":["css", "javascript", "mongoose", "node"]
}
Similarly, you can create other types of primitive arrays and document arrays in the mongoose schema as:
({
toys: [ToySchema],
buffers: [Buffer],
strings: [String],
numbers: [Number]
// ... etc
});
Try changing the schema to
var personSchema = new mongoose.Schema({
tags: [{type: String}]
});
or you can use Mixed type
var personSchema = new mongoose.Schema({
tags: mongoose.Schema.Types.Mixed
});
EDIT
i think the problem is with assignment. Use:
person.tags.push("string to push");
On Schema
techs: Array
On Postman
"techs": ["express","rect","html","css","scss"]
On DB (MongoDB)
"techs" : [
"epxress",
"rect",
"html",
"css",
"scss"
]
var personSchema = new mongoose.Schema({
tags: [{type: String}]
});
Use this in the schema.
Saving the Array:
var etc = new modename({yourprimaryid: primaryid});
for (var i = 0; i < tag.length; i++) {
etc.tag.push(tag[i]);
}
etc.save(function(err) {
//whatever you want here
}
Define a Schema:
const schema = new Schema({
name: { type: String, required: true },
tags: [String]
});
In postman add each element separately using the array syntax below
name:Thing
tags[]:task
tags[]:other
tags[]:thing
Return Data:
{
"__v": 0,
"name": "Thing",
"_id": "5a96e8d0c7b7d1323c677b33",
"tags": [
"task",
"other",
"thing"
]
}
this will also work
var personSchema = new mongoose.Schema({
tags: {
type: [String], default: []
}
});
Firstly, as many people have noted, the schema needs to change to indicate that the tags field is intended to hold an array of strings, and not just a single one. So that needs to change to:
var personSchema = new mongoose.Schema({
tags: [String]
});
The other thing you need to keep in mind (and which caused me a lot of trouble), is that when saving, make sure to use a fresh array for the tags field. For example, this won't work:
person.tags[0] = "new tag";
person.save();
Instead, you need to do something like:
person.tags = person.tags.slice(); // Clone the tags array
person.tags[0] = "new tag";
person.save();
Hope this helps.
I had a simialr problem,
In the model, do this :
tags : {[String], default: undefined}
So that it defaults to undefined unstead of an empty array,
and instead of this:
const person = new Person({
tags : req.body.tags
});
Do this :
const person = new Person();
person.tags = req.body.tags;
My requirement;
ingredients: Type Array of Strings
Solution:
ingredients: {
type: [String],
},
const productSchema = new mongoose.Schema(
{
name: {
type: String,
},
description: {
type: String,
},
price: {
type: String,
},
categoryId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
},
sellerId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Seller",
},
discount: {
type: String,
},
status: {
type: String,
default: "active",
enum: ["active", "inactive", "deleted"],
},
images: {
type: Array,
required: true,
},
},
{ timestamps: true }
);