Mongoose - Save array of strings - node.js

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 }
);

Related

Mongoose - array of enum strings

I have a Schema that has a property with the type of array of strings that are predefined.
This is what I've tried to do:
interests: {
type: [String],
enum: ['football', 'basketball', 'read'],
required: true
}
The thing is that when I'm trying to enter a wrong value that isn't defined on the enum, to the array it wouldn't validate it with the enum list.
for example, this would pass which it shouldn't:
{ "interests": ["football", "asdf"] }
because "asdf" isn't predefined in the enum list it shouldn't pass the validation but unfortunately, it passes the validation and saves it.
I've tried to check this thing with a string type of values instead of an array of strings and it works.
for example:
interests: {
type: String,
enum: ['football', 'basketball', 'read'],
required: true
}
for example, this is failing as expected:
{ "interest": "asdf" }
In conclusion, I need a schema's property with a type of array of strings that would check it's elements based on predefined values
Is the most effective way to achieve this goal is by using the validate method or there is a better way?
Quoting from here:
const SubStrSz = new mongoose.Schema({ value: { type: String, enum: ['qwerty', 'asdf'] } });
const MySchema = new mongoose.Schema({ array: [SubStrSz] });
Using that technique you will able to validate values inside of your array.
You can try a custom validation?Like this
const userSchema = new Schema({
phone: {
type: String,
validate: {
validator: function(v) {
return /\d{3}-\d{3}-\d{4}/.test(v);
},
message: props => `${props.value} is not a valid phone number!`
},
required: [true, 'User phone number required']
}
});
this is the docs:
https://mongoosejs.com/docs/validation.html
Here distributers will be array of distributerObj, similarly you can define object of any type.
const distributerObj = new Schema({
"dis_id": {
"type": "String"
},
"status": {
"type": "String"
}
});
const productSchema = new Schema({
"distributers": {
"type": [distributerObj]
}
});
use ref to make a relation to the defined enum schema
const actionsEnums = new Mongoose.Schema({value: { type: String, enum:["account-deletion","account-update"]}});
const Privilege = new Mongoose.Schema(
{
userLevel: { type: String, required: true },
actions: [{type: String, refs: actionsEnums}],
})

How to specify schema attributes for mongoose subdocument?

I'm building a plugin and need to specify attributes for one of the subdocument collection fields. Someone mentioned that statics are the official way of doing this.
So, instead of...
var familySchema = mongoose.Schema({
surName : { type: String },
members : { type: [ personSchema ], minLength: 1, maxLength: 10 }
});
... it would be this ...
var familySchema = mongoose.Schema({
surName : { type: String },
members : [ personSchema ]
});
familySchema.static('minLength', 1);
familySchema.static('maxLength', 10);
I realize there is no minLength or maxLength. The plugin would handle this.
I've been looking through the docs and can't find anything that mentions the official way. Is there a benefit of one versus the other?
You're right, there is no such thing as minLength for ref array. You have to use custom validation
var validateFamilyLength = function (members) {
return members.length > 1 && members.length < 10;
};
var familySchema = mongoose.Schema({
surName : { type: String },
members : {
type: [personSchema],
validate: [validateFamilyLength, 'Not valid members count']
}
});
But i strongly recommend to use refs instead
var familySchema = mongoose.Schema({
surName : { type: String },
members : [{
type: mongoose.Schema.ObjectId,
ref: 'Person',
validate: [validateFamilyLength, 'Not valid members count']
}]
});

Nest mongoose schema within itself?

Is it possible with mongoose to create a schema, call it Folders and it has a property within called subfolders that is an array of nested Folder subdocs?
const mongoose = require('mongoose')
let SubSchema = mongoose.Schema({
name: { type: String, required: true }
})
let FolderSchema = mongoose.Schema({
name: { type: String, required: true },
children: [ SubSchema ]
})
I know I can nest subdocs in an array by referencing another schema similar to what is shown above. What I'm looking to do though is reuse FolderSchema within itself. Obviously this causes an issue because at the time of creation of the schema, FolderSchema doesn't exist. Is there a better way to do this? Is there a way to recursively nest documents using the same schema?
I know I could have the array be a list of ObjectId that reference a collection but I was hoping to just keep it all nested as documents. I guess if I did populate() to let it resolve the doc ids, that would essentially be the same thing. Just wondering if there is another method I wasn't aware of.
I haven't tried this personally, but I have read this can be achieved by simply referencing this in the field you want to reference the current model.
The model you would like to will look something like this,
const mongoose = require('mongoose')
const FolderSchema = mongoose.Schema({
name: { type: String, required: true },
type: { type: String, enum: ['file', 'directory'],
children: [ this ]
})
const FolderModel = mongoose.model('Folder', FolderSchema);
Hope that helps!
look you need to clarify your question a little bit but as much as i understood from the question, yes it can be done in this way :
var mongoose = require('mongoose');
var FolderSchema = new mongoose.Schema({
SubFolders = [ type:monogoose.Schema.Types.ObjectId, ref : 'Folders']
});
var folder = mongoose.model('Folders',FolderSchema);
module.exports = folder;
This shall work for you.
So for infinite object having children, I did it like so:
mongoose schema:
const itemSchema = new mongoose.Schema({
name: String,
items: {
type: [this],
default: undefined
}
}, { _id: false })
const mainSchema = new mongoose.Schema({
name: String,
items: {
type: [itemSchema],
default: undefined
}
})
output example:
[
{
_id: '62a72d6915ad7f79d738e465',
name: 'item1',
items: [
{
name: 'item1-item1',
items: [
{
name: 'item1-item1-item1'
},
{
name: 'item1-item1-item2'
},
{
name: 'item1-item1-item3'
},
]
},
{
name: 'item1-item2'
}
]
},
{
_id: '62a72d6915ad7f79d738e467',
name: 'item2'
},
{
_id: '62a72d6915ad7f79d738e467',
name: 'item3',
items: [
{
name: 'item3-item1'
}
]
}
]

How can I auto-case key names when saving to Mongoose?

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

Mongoose Relationship Populate Doesn't Return results

var SecuritySchema = new Mongoose.Schema({
_bids: [{
type: Mongoose.Schema.Types.ObjectId,
ref: 'BuyOrder'
}],
_asks: [{
type: Mongoose.Schema.Types.ObjectId,
ref: 'SellOrder'
}]
});
var OrdersSchema = new Mongoose.Schema({
_security: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Security'
},
price: {
type: Number,
required: true
},
quantity: {
type: Number,
required: true
}
});
// declare seat covers here too
var models = {
Security: Mongoose.model('Security', SecuritySchema),
BuyOrder: Mongoose.model('BuyOrder', OrdersSchema),
SellOrder: Mongoose.model('SellOrder', OrdersSchema)
};
return models;
And than when I save a new BuyOrder for example:
// I put the 'id' of the security: order.__security = security._id on the client-side
var order = new models.BuyOrder(req.body.order);
order.save(function(err) {
if (err) return console.log(err);
});
And attempt to re-retrieve the associated security:
models.Security.findById(req.params.id).populate({
path: '_bids'
}).exec(function(err, security) {
// the '_bids' array is empty.
});
I think this is some sort of naming issue, but I'm not sure, I've seen examples here and on the moongoose website that use Number as the Id type: http://mongoosejs.com/docs/populate.html
The ref field should use the singular model name
Also, just do:
models.Security.findById(req.params.id).populate('_bids').exec(...
My main suspicion given your snippet at the moment is your req.body.order has _security as a string instead of an array containing a string.
Also, you don't need an id property. Mongodb itself will automatically do the _id as a real BSON ObjectId, and mongoose will add id as a string representation of the same value, so don't worry about that.
While I don't understand your schema (and the circular nature of it?), this code works:
var order = new models.BuyOrder({ price: 100, quantity: 5});
order.save(function(err, orderDoc) {
var security = new models.Security();
security._bids.push(orderDoc);
security.save(function(err, doc) {
models.Security.findById({ _id: doc._id })
.populate("_bids").exec(function(err, security) {
console.log(security);
});
});
});
It:
creates a BuyOrder
saves it
creates a Security
adds to the array of _bids the new orderDoc's _id
saves it
searches for the match and populates
Note that there's not an automatic method for adding the document to the array of _bids, so I've done that manually.
Results:
{ _id: 5224e73af7c90a2017000002,
__v: 0,
_asks: [],
_bids: [ { price: 100,
quantity: 5,
_id: 5224e72ef7c90a2017000001, __v: 0 } ] }

Resources