Error adding array to MongoDB / Mongoose collection - arrays

I am trying to add an array to a Mongo document, and I'm getting the error "Cast to Array failed for value "[object Object],[object Object]" at path "vendors""
Here is my model:
module.exports = {
attributes: {
vendors: {
type: [String]
},
description: {
type: String
}
}
};
Here is my code to create:
var vendors = ko.observableArray(['foo','bar']);
var desc = ko.observable('yadda yadda yadda');
var dto = {
data: {
vendors: vendors(),
description: description()
}
};
DataService.quoteRequest.create(dto);

Musical Shore,
I'm not familiar with knockout.js, but in regards to Mongoose it doesn't look like you are defining your Schema and Model. You would need to do the following:
Define Schema
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var attributesSchema = new Schema({
vendors: [String],
description: String
});
Create a Model
var Attributes = mongoose.model('Attribute',attributesSchema);
Create and Save a Document
//create an attribute document
var attribute = new Attributes(
{
vendors: vendorsArray,
description: desc
}
);
attribute.save(function(err){
if(!err) console.log('Success');
}

Related

Collection is not getting created even after creating the model using mongoose

I have created a model using mongoose.
However, it is not creating collection in mongodb.
My connection is working fine though to the mongodb
var mongoose = require("mongoose"),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
const conn = require('app/connection');
var notesSchema = new Schema({
_id: ObjectId,
message: String,
});
var dataSchema = new Schema({
_id: ObjectId,
dataId: String,
dataName: String,
notes: [notesSchema]
}, {
collection: 'dataCols'
});
var dataCol = conn.on('cg').model('dataCol', dataSchema);
I have also tried acting against the model (find, insert etc) but the collection is not created automatically.
I am connecting to a replica set

Save Array Mongoose

I can't save an array of strings into my DB using Mongoose
The schema:
const TrombinoSchema = new mongoose.Schema({
Name: [String]
});
const Trombino = mongoose.model('Trombino', TrombinoSchema);
and for save:
var trombino = new Trombino ({
Name: req.query.name
});
trombino.save(callback);
Array.isArray(req.query.name) => true;
I tried different statement
Name: [{type : String}] , Name: [{type : String}] or Name: []
but in my DB i found the empty Array
In the trombino schema you need to change the name field:
const mongoose = require('mongoose');
const TrombinoSchema = new mongoose.Schema({
Name: {type:[String]}
});
const Trombino = mongoose.model('Trombino', TrombinoSchema);
let trombinoItem = new Trombino({
Name: ['test name 1','test name 2','test name 3']
});
trombinoItem.save().then((result)=>{
console.log(result);
});
Here the print of execution:
This the data in the database. You can see the name as a array of string:

Mongo schema, array of string with unique values

I'm creating the schema for a mongo document and I can do everything except prevent duplicates in a non-object array.
I'm aware of the addToSet, but I'm referring to Mongo Schema.
I don't want to check on Update using $addToSet, rather I want this to be part of my schema validation.
Example below.
let sampleSchema = {
name: { type: 'String', unique: true },
tags: [{ type: 'String', unique: true }]
}
The above snippet prevents name from having duplicate values. It allows tags to be stored as a string array.
But.. I cannot limit the array to be unique strings.
{ name: 'fail scenario', tags: ['bad', 'bad', 'array']}
I'm able to insert this record which should be a fail scenario.
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const _ = require('underscore');
let sampleSchema = new mongoose.Schema({
name: {
type: 'String',
unique: true
},
tags: [{
type: 'String'
}]
})
sampleSchema.pre('save', function (next) {
this.tags = _.uniq(this.tags);
next();
});
const Sample = mongoose.model('sample', sampleSchema, 'samples');
router.post('/sample', function (req, res, next) {
const sample = new Sample(req.body);
sample.save()
.then((sample) => {
return res.send(sample);
})
.catch(err => {
return res.status(500).send(err.message);
})
});
I've come to the conclusion that this is impossible to do via Mongoose Schema.
JSON schema is done like so.
let schema = {
name: { type: 'string' }
tags: {
type: 'array',
items: { type: 'string', uniqueItems: true }
}
}
I'll validate with JSON schema before creating Mongo Document.
This method builds on Med's answer, handles references, and done completely in scheme validation.
let sampleSchema = new mongoose.Schema({
strings: [{type: 'String'}],
references: [{type: mongoose.Schema.Types.ObjectId, ref: 'Reference'],
});
sampleSchema.pre('save', function (next) {
let sample = this;
sample.strings = _.uniq(sample.strings, function(i) {return (i._id) ? i._id.toString() : i;});
sample.references = _.uniq(sample.references, function(i) {return (i._id) ? i._id.toString() : i;});
return next();
});
I'm a little late, but maybe this will help someone in the future.
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
},
reference: {
type: [mongoose.Schema.Types.ObjectId],
ref: 'SomeOtherSchema',
// Add a custom validator.
validate: {
// The actual validator function goes here.
// "arr" will be the value that's being validated (so an array of
// mongoose new ObjectId statements, in this case).
validator: arr => {
// Convert all of the items in the array "arr", to their string
// representations.
// Then, use those strings to create a Set (which only stores unique
// values).
const s = new Set(arr.map(String));
// Compare the Set and Array's sizes, to see if there were any
// duplicates. If they're not equal, there was a duplicate, and
// validation will fail.
return s.size === arr.length;
},
// Provide a more meaningful error message.
message: p => `The values provided for '${ p.path }', ` +
`[${ p.value }], contains duplicates.`,
}
},
});
The above commented code should be pretty self explanatory.
With the newer version(s) of MongoDB, you can use $addToSet to append to an array if and only if the new value is unique compared to the items of the array.
Here's the reference: https://www.mongodb.com/docs/manual/reference/operator/update/addToSet/
Here's an example:
const SampleSchema = new mongoose.Schema({
tags: [String]
});
const Sample = mongoose.model('Sample', SampleSchema);
// append to array only if value is unique
Sample.findByIdAndUpdate({_id: 1, {$addToSet: {tags: "New Tag"}}});
This will effectively update the tags if the "New Tag" is not already present in the tags array. Otherwise, no operation is done.

Mongoose doesn't create subdocument from JSON array

I'm trying to write a JSON object that contains both first-level data along with arrays into MongoDB.
What happens instead is all first-level data is stored, but anything contained in an array isn't. When logging the data the server receives, I see the entire object, which leads me to believe there's something wrong with my Mongoose code.
So for example if I send something like this:
issueId: "test1",
issueTitle: "testtest",
rows: [
{order:1,data: [object]},
{order:2,data: [object]},
]
Only the following gets stored:
issueId: "test1",
issueTitle: "testtest",
lastUpdated: Date,
I have the following model for Mongo:
//model.js
var mongoose = require('mongoose');
var model = mongoose.Schema({
issueId : String,
issueTitle : String,
lastUpdated : {type: Date, default : Date.now},
rows : [{
order : Number,
data : [
{
title : String,
text : String,
link : String,
}
]
}]
});
module.exports = mongoose.model('Model', model);
And the routing code, where I believe the problem likely is:
//routes.js
const mongoose = require('mongoose');
const Model = require('./model.js');
...
app.post('/api/data/update', function(req, res) {
let theData = req.body.dataToInsert;
console.log(JSON.stringify(theData,null,4));
Model.findOneAndUpdate(
{issueId : theData.issueId},
{theData},
{upsert: true},
function(err,doc){
if(err) throw err;
console.log(doc);
});
});
As well, here's the part of the Angular controller storing the data. I don't think there's any problem here.
pushToServer = function() {
$http.post('/api/data/update',{
dataToInsert : $scope.dataObject,
}).then(function successCallback(res){
console.log("all good", JSON.stringify(res,null,3));
}, function errorCallback(res){
console.log("arg" + res);
});
}
Look at the first question in the mongoose FAQ:
http://mongoosejs.com/docs/faq.html
Mongoose doesn't create getters/setters for array indexes; without them mongoose never gets notified of the change and so doesn't know to persist the new value. The work-around is to use MongooseArray#set available in Mongoose >= 3.2.0.
// query the document you want to update
// set the individual indexes you want to update
// save the document
doc.array.set(3, 'changed');
doc.save();
EDIT
I think this would work to update all of the rows. I'd be interested to know if it does work.
let rowQueries = [];
theData.rows.forEach(row => {
let query = Model.findOneAndUpdate({
issueId: theData.issueId,
'row._id': row._id
}, {
$set: {
'row.$': row
}
});
rowQueries.push(query.exec());
});
Promise.all(rowQueries).then(updatedDocs => {
// updated
});

How to query mongoose by property that is and array item

I have a mongoose model that looks like this:
var mongoose = require('mongoose')
, Schema = mongoose.Schema;
var PictureSchema = new Schema({
listId: { type: Array, required: true },
thumb: { type: String, required: true },
large: { type: String, required: true }
});
var Picture = module.exports = mongoose.model('Picture', PictureSchema);
I am trying to update instances of this model in my router by looking up a Picture via the "listId" property. Like this:
app.put('/pictures/append', function(req, res) {
var targetListId = req.body.targetListId
, currentListId = req.body.currentListId;
Picture
.find({ listId: currentListId }, function (err, picture) {
console.log('found pic', picture);
picture.listId.push(targetListId);
picture.save(function(err, pic) {
console.log('pic SAVED', pic);
});
});
});
"currentListId" is a string, and listId is an array of currentListId's. Maybe this isn't the correct way to query a a property that is an array?
I am getting an error:
TypeError: Cannot call method 'push' of undefined
On the line:
picture.listId.push(targetListId);
But when I look up the picture models in mongo, they DO have listId arrays and some DO contain the item "currentListId" that I am using for my query.
I tried using $elemMatch and $in but I don't know if I was using them correctly.
Any idea if I am just writing my query wrong?
Specifying an Array typed field in your schema is equivalent to Mixed which tells Mongoose that field could contain anything. Instead, change your schema to something like this:
var PictureSchema = new Schema({
listId: [String],
thumb: { type: String, required: true },
large: { type: String, required: true }
});

Resources