Why is this insert into a schema attached collection invalid? - meteor

Let's say, we have this schema :
Schemas.MyCollection = new SimpleSchema({
something: {
type: Object
}
});
I want to insert something into MyCollection. For example :
var myobj = {
aaaaaa: 11111,
bbbbbb: 22222
};
MyCollection.insert({something: myobj});
We end up with this :
{
_id: "someId",
something: {}
}
When I disable simple schema checking (collection2), everything works as one expected.
Simple-schema did not report an error (collection2) so why it is invalid?

#Seraph your schema is wrong
Schemas.MyCollection = new SimpleSchema({
something: {
type: Object
},
'something.aaaaa': {
type: String
}
});
and so on you have to write every property the object has or you can do blackbox: true if you don't want to validate the object:
something: {
type: Object,
blackbox: true
}
Also if it's server-side operation you can do myCollection.insert(doc, {validate: false});
just read the docs https://atmospherejs.com/aldeed/collection2 :)

Here is the reference to help you understand more:
https://github.com/aldeed/meteor-simple-schema#blackbox

Related

Meteor collections: upsert w/ array

Forms of this question have been asked a few times, but I've been unable to find a solution:
I have a schema like this (simplified):
StatusObject = new SimpleSchema({
statusArray: [statusSchema]
});
where statusSchema is
{
topicId:{
type: String,
optional: true
},
someInfo:{
type: Number,
optional: true,
decimal: true
},
otherInfo:{
type: Number,
optional: true
}
}
I am trying to upsert - with the following meteor method code:
var upsertResult = BasicInfo.update({
userId: this.userId,
statusArray: {
$elemMatch: { topicId : newStatus.topicId }
}
}, {
$set: {
"statusArray.$.topicId": newStatus.topicId,
"statusArray.$.someInfo": newStatus.someInfo,
"statusArray.$.otherInfo": newStatus.otherInfo
}
}, {
multi: true,
upsert: true
});
But I keep getting an error: statusArray must be an array
I thought by adding the $, I was making sure it is recognized as an array? What am I missing?
It seems (after your clarification comments), that you want to find a document with particular userId and modify its statusArray array using one of these scenarios:
Update existing object with particular topicId value;
Add a new object if the array doens't have one with particular topicId value.
Unfortunately, you can't make it work using just one DB query, so it should be like this:
// try to update record
const updateResult = BasicInfo.update({
userId: this.userId,
'statusArray.topicId': newStatus.topicId
}, {
$set: {
"statusArray.$": newStatus
}
});
if (!updateResult) {
// insert new record to array or create new document
BasicInfo.update({
userId: this.userId
}, {
$push: {
statusArray: newStatus
},
$setOnInsert: {
// other needed fields
}
}, {
upsert: true
});
}
Your code is treating StatusArray as an object,
Before you do the upsert, build the status array first, assuming that your current value is currentRecord
newStatusArray = currentRecord.statusArray
newStatusArray.push({
topicId: newStatus.topicId,
someInfo : newStatus.someInfo,
otherInfo: newStatus.otherInfo
})
and in the upsert, simply refer to it like this
$set: { statusArray: newStatusArray}

Validation error with simple-schema

I'm trying to insert an array into an object and I'm not having any luck. I think the schema is rejecting it based on validation but I'm not sure why. If I console.log(this.state.typeOfWork) and check typeof it states its an Object which contains:
(2) ["Audit - internal", "Audit - external"]
0: "Audit - internal"
1: "Audit - external"
My collection after an update contains:
"roleAndSkills": {
"typeOfWork": []
}
Example: Schema
roleAndSkills: { type: Object, optional: true },
'roleAndSkills.typeOfWork': { type: Array, optional: true },
'roleAndSkills.typeOfWork.$': { type: String, optional: true }
Example: update
ProfileCandidate.update(this.state.profileCandidateCollectionId, {
$set: {
roleAndSkills: {
typeOfWork: [this.state.typeOfWork]
}
}
});
Simple schema has some problems with validation on Objects or Arrays, i had the same problem in a recent app i developed.
What can you do?
well, what i did, on the Collections.js file, when you are saying:
typeOfWork:{
type: Array
}
Try adding the property blackbox:true, like this:
typeOfWork:{
blackbox: true,
type: Array
}
This will tell your Schema that this field is taking an Array, but ignore further validation.
The validation i made was on main.js, just to be sure i had no empty array and the data was plain text.
As requested here is my update method, im my case i used objects not arrays but it works the same way.
editUser: function (editedUserVars, uid) {
console.log(uid);
return Utilizadores.update(
{_id: uid},
{$set:{
username: editedUserVars.username,
usernim: editedUserVars.usernim,
userrank: {short: editedUserVars.userrank.short,
long: editedUserVars.userrank.long},
userspec: {short: editedUserVars.userspec.short,
long: editedUserVars.userspec.long},
usertype: editedUserVars.usertype}},
{upsert: true})
},
here it the collection schema
UtilizadoresSchema = new SimpleSchema({
username:{
type: String
},
usernim:{
type: String
},
userrank:{
blackbox: true,
type: Object
},
userspec:{
blackbox: true,
type: Object
},
usertype:{
type: String
}
});
Utilizadores.attachSchema(UtilizadoresSchema);
Hope it helps
Rob
typeOfWork is an Array. You should push your value in it :
$push: {
"roleAndSkills.typeOfWork": this.state.typeOfWork
}
for multiple values :
$push: {
"roleAndSkills.typeOfWork": { $each: [ "val1", "val2" ] }
}
mongo $push operator
mongo dot notation
You state that this.state.typeOfWork is an array (of strings) but then when you .update() your document you are enclosing it in square brackets:
ProfileCandidate.update(this.state.profileCandidateCollectionId, {
$set: {
roleAndSkills: {
typeOfWork: [this.state.typeOfWork]
}
}
});
Simply remove the redundant square brackets:
ProfileCandidate.update(this.state.profileCandidateCollectionId, {
$set: {
roleAndSkills: {
typeOfWork: this.state.typeOfWork
}
}
});
Also since your array is just an array of strings you can simplify your schema a bit by declaring it as such with [String] for the type:
'roleAndSkills.typeOfWork': { type: [String] }
Note furthermore that objects and arrays are by default optional so you can even omit the optional flag.

Meteor collection2 type Object

I'm trying to create a field modifiedBy with type: Object (to Meteor users).
I see you can setup blackbox: true for a Custom Object, but if I want to setup to a specific Object say a Group (collection) field modifiedBy is the logged in user, any pointers/help is greatly appreciated.
Thanks
As far as I see it, you have two options:
Store user-ids there with type: String
Denormalize it as you proposed
Denormalize it as you proposed
To denormalize it you can do something like this inside your schema:
...
modifiedBy: {
type: object
}
'modifiedBy._id': {
type: String,
autoValue: function () {
return Meteor.userId()
}
}
'modifiedBy.username': {
type: String,
autoValue: function () {
return Meteor.user().username
}
}
...
As you pointed out, you'd want to update these properties when they change:
server-side
Meteor.users.find().observe({
changed: function (newDoc) {
var updateThese = SomeCollection.find({'modifiedBy.username': {$eq: newDoc._id}})
updateThese.forEach () {
SomeCollection.update(updateThis._id, {$set: {name: newDoc.profile.name}})
}
}
})
Store user-ids there with type: String
I'd recommend storing user-ids. It's cleaner but it doesn't perform as well as the other solution. Here's how you could do that:
...
modifiedBy: {
type: String
}
...
You could also easily write a Custom Validator for this. Now retrieving the Users is a bit more complicated. You could use a transform function to get the user objects.
SomeCollection = new Mongo.Collection('SomeCollection', {
transform: function (doc) {
doc.modifiedBy = Meteor.users.findOne(doc.modifiedBy)
return doc
}
})
But there's a catch: "Transforms are not applied for the callbacks of observeChanges or to cursors returned from publish functions."
This means that to retrieve the doc reactively you'll have to write an abstraction:
getSome = (function getSomeClosure (query) {
var allDep = new Tacker.Dependency
var allChanged = allDep.changed.bind(allDep)
SomeCollection.find(query).observe({
added: allChanged,
changed: allChanged,
removed: allChanged
})
return function getSome () {
allDep.depend()
return SomeCollection.find(query).fetch()
}
})

SimpleSchema invalid keys with nested autoValue

I have a schema like so (fluff cut out):
Schemas.people = new SimpleSchema({
note: {
type: [Schemas.notes],
optional: true,
defaultValue: []
},
updates: {
type: [Schemas.updates],
optional:true,
autoValue:function(){
if (this.isInsert) {
return [{
at: new Date,
user_id: this.userId
}];
}
return {
$push:{
at: new Date,
user_id: this.userId
}
}
}
}
});
And the notes schema looks like:
Schemas.notes = new SimpleSchema({
note: {
type: String,
autoform: {
afFieldInput:{
type:"textarea"
}
},
optional: true
},
updates: {
type: [Schemas.updates],
optional:true,
autoform:{
omit:true
},
autoValue:function(){
if (this.isInsert) {
return [{
at: new Date,
user_id: this.userId
}];
}
return {
$push:{
at: new Date,
user_id: this.userId
}
}
}
}
});
And the updates schema is super simple:
Schemas.updates = new SimpleSchema({
at: {
type: Date
},
user_id:{
type: Meteor.ObjectID
}
});
The "updates" field on the people schema saves the date/user id as expected when an update is made. However, it fails on the notes schema:
SimpleSchema invalid keys for "blablabla" context:
0: Object
name: "note.0.updates.0.at"
type: "keyNotInSchema"
value: Mon May 11 2015 11:57:58 GMT-0400 (Eastern Daylight Time)
1: Object
name: "note.0.updates.0.user_id"
type: "keyNotInSchema"
value: "abcd1234"
I believe that the "name" should look like "people.note.0.updates.0.at" but I'm unsure that this assumption is correct and I'm completely unsure how to go about making that happen.
Update:
Code used to update people
{{#autoForm collection="people" id=formId type="update" class="update" autocomplete="off" doc=getDocument autosave=true template="quickform"}}
{{> afQuickField name='note' template="quickform" }}
{{/autoForm}}
formId returns a randomish ID string and getDocument passes in the correct collection.
Schemas.notes._schemaKeys does not list the at and user_id fields... but Schemas.people._schemaKeys does.
People schema shows: [..., "updates.$.at", "updates.$.user_id", ...]
Notes schema shows: ["note", "updates", "updates.$"]
How bizarre.
Note that Meteor uses standard JavaScript syntax and therefore has the same restrictions, for example as you already realized order of code is important.
Let's have a look.
Schemas.notes = new SimpleSchema({
updates: {
type: [Schemas.updates]
}
}
There are no nested functions in this code, therefore every code of line will be executed, before Meteor continues with the next Schema definition. Schema.updates will be dereferenced immediately, although it isn't set yet. type will be an array containing null and that finally makes SimpleSchema assume that no fields are allowed at all.
The issue is with the order in which the schemas are declared. Which I suppose makes sense? I was declaring "notes" before "updates", and "people" last. Putting "updates" first fixed the issue completely.
I'm going to report this as a possible bug to the collection repo.

How to change a schema key?

I have a schema defined below and how can I change the predefined schema key (summary: key) via meteor template?
Schemas.Books = new SimpleSchema(
{
summary: {
type: String
}
}
);
For instance, I want to change this key through a session variable that was defined by router or through a user input.
Not Sure, Try this
If your schema is like this
Books = new SimpleSchema(
{
summary: {
type: String
}
}
);
then in tempalte helpers,
Books._schema.summary.type = function() {
return Session.get("typeValue");
};
In my project I have schema like this
RegisterSchema = new SimpleSchema({
name: {
type: String
},
email: {
type: String,
regEx: SimpleSchema.RegEx.Email
},
password: {
type: String,
label: "Password",
min: 8
},
confirmPassword: {
type: String,
label: "Confirm Password",
min: 8,
custom: function () {
if (this.value !== this.field('password').value) {
return "passwordMismatch";
}
}
}
});
and I'm dynamically setting optional value for email like
RegisterSchema._schema.email.optional = function() { return true };
this worked for me.
All d best
This is not the thing that I'm trying to do but I have learned a new trick : )
I want to change the schema key that I described above like this.
Books = new SimpleSchema(
{
bookName: {
type: String
}
}
);
Changing summary: with bookName:
Actually I want to define schema keys dynamically with respect to user information (userId, userName etc.).

Resources