How can I store userid's of meteor account-users in a collection2 array. My definition of schema looks like. Will this work?
I wanted an approach to add associated users to a collection. Whereby I wanted to control who are allowed to see this particular document of the collection. Is there a better way to do this than how I'm modelling.
name: {
type: String,
max: 100,
optional: false
},
billingAddress: {
type: AddressSchema
},
shippingAddress: {
type: [AddressSchema]
},
type: {
type: String,
max: 20,
allowedValues: ['Vendor', 'Customer', 'Self', 'Employee'],
optional: false
},
phones: {
type: [String]
},
emails: {
type: [String],
regEx: SimpleSchema.RegEx.Email
},
website: {
type: String,
regEx: SimpleSchema.RegEx.url
},
isCustomer: {
type: Boolean,
optional: false
},
isVendor: {
type: Boolean,
optional: false
},
isSearchable: {
type: Boolean,
optional: false
},
associatedUsers: {
type: [Meteor.users]
}
Each document receives an _id automatically. It isn't recommended to put _id in the schema. Not sure it will even work for you.
If you want to know if it will work, I recommend trying it yourself! You have the schema already.
For user permissions, I recommend using the alanning:roles package. That should solve your issues.
Related
I am wondering which is better in terms of performance.
Let's say i have this Menu Schema
{
restaurant: { type: String, unique: true, required: true },
color: { type: String, required: true },
}
And i have this Plate Schema
{
name: { type: String, unique: true, required: true },
}
If i want to get all the plates from the Menu, which option is better?
Associate the Menu with plates like this:
{
restaurant: { type: String, unique: true, required: true },
color: { type: String, required: true },
plates: [
type: Schema.Types.ObjectId,
ref: 'plate'
]
}
Or like this:
{
name: { type: String, unique: true, required: true },
menu: {
type: Schema.Types.ObjectId,
ref: 'plate'
}
}
With the first option, i would use Menu.findById(id).populate('plates') to get the menu and all the plates associted with it. But with the second option, i would use Plate.find({menu: menu_id})
So, which is faster considering that i could have any amount of plates?
And if they are the same, is there a better way of associate Plate and Menu and get plates from Menu?
In terms of the performance, the second one would be faster because the first one would do the $lookup behind the scene (when doing populate), which is actually additional database call.
I would suggest the third option, which would be the best in your use case. That is embedding the plate documents inside the menu document.
{
restaurant: { type: String, unique: true, required: true },
color: { type: String, required: true },
plates: [
{
name: { type: String }
}
]
}
This way, menu document will contain all the information you needed and you can fetch everything with one database call.
Note: There is a limitation of 16MB for each document in the MongoDB. You can for sure store thousands or tens of thousands plates in each menu without a problem. But if you need to store much more in each menu, then you should go with some of your initial setups.
I want to do a big project API where people can login with Google, send the token and when logged in do some actions saved to Mongo DB.
The problem is the tables structure, or schema. So, I want to do a good app scalable.
I have some tables like Users (with users loggin information) and I want user save his tasks, memories, works and more for him self, and when logged in in another device, get this information and modify.
Do I need to do every table for every user or use same table filtered by user?
For example, I have now this model for product:
const ProductoSchema = Schema({
nameOfProduct: {
type: String,
require: [ true, 'Required name' ],
unique: true
},
state: {
type: Boolean,
default: true,
required: true
},
userOwner: {
type: Schema.Types.ObjectId,
ref: 'Usuario',
required: true
},
priece: {
type: Number,
default: 0
},
category: {
type: Schema.Types.ObjectId,
ref: 'Category',
required: true
},
description: {
type: String
},
disponibility: {
type: Boolean,
default: true
},
img: {
type: String
},
});
Is this correct or this is not scalabe and I need to do all tables for every user?
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'm currently trying to figure out at mongodb what's the best way in terms of performance cost and redundancy the best way of building a big document data schema. The final JSON from my rest -> app will be likely how it is structured.
Now internally the data will not be used as many to many that's why i binded it into a single document. Only the id will be used as a reference in another collections.
What you guys think, is it better to spit as relational way, with multiple collection to store the content inside of deliverable and use reference or just embedded. (since NoSQL has no joins i though this way will speed up)
Current using mongoose at node app
The Schema:
projectSchema = new Schema({
name: {
type: String,
required: true,
minlength: 3,
maxlength: 50
},
companyId: {
type: mongoose.Types.ObjectId,
ref: 'companies',
required: true
},
deleted: {
type: Number,
enum: [0, 1],
default: 0
},
predictedStartDate: {
type: Date,
default: ""
},
predictedEndDate: {
type: Date,
default: ""
},
realStartDate: {
type: Date,
default: ""
},
realEndDate: {
type: Date,
default: ""
},
//not final version
riskRegister: [{
name: String,
wpId: {
type: mongoose.Types.ObjectId,
ref: 'projects.deliverables.workPackages.id',
required: true
},
probability: String,
impact: String,
riskOwner: String,
response: String,
duration: String,
trigger: String,
status: String,
plannedTimming: String
}],
deliverables: [{
body: String,
workPackages: [{
body: String,
activities: [{
body: String,
tasks: [{
content: String,
properties: [{
dependecies: Array,
risk: {
type: Number,
enum: [0,1],
required: true
},
estimatedTime: {
type: Number,
required: true
},
realTime: {
required: true,
default: 0,
type: Number
},
responsible: {
id: {
type: Number,
default: -1
},
type: {
type: String,
enum: [0, 1], //0 - user, 1 - team
default: -1
}
},
materialCosts: {
type: Number,
default: 0
},
status: {
type: Number,
default: 0
},
approval: {
type: Number,
default: 0
},
startDate: {
type: Date,
default: ""
},
finishDate: {
type: Date,
default: ""
},
endDate: {
type: Date,
default: ""
},
userStartDate: {
type: Date,
default: ""
},
endStartDate: {
type: Date,
default: ""
},
taskNum: {
type: Number,
required: true
},
lessonsLearn: {
insertedAt: {
type: Date,
default: Date.now
},
creatorId: {
type: mongoose.Types.ObjectId,
ref: 'users',
required: true
},
situation: {
type: String,
required: true
},
solution: {
type: String,
required: true
},
attachments: Array
}
}]
}]
}]
}]
}]
})
The only concern I would raise would be regarding deliverables. If in the future there is a use case to do some CRUD operation regarding activities or tasks on the workPackage, the mongodb position operator $ does not support inner arrays, so you would be forced to extract all the deliverables and in memory iterate over all and only after update the deliverables.
My sugestion would be to support only arrays in the first level on the object. The inner objects should be moduled in separate collection ( activities and tasks ). In latest versions of mongodb you now have support to transactions so you can implement ACID on your operations against database, so the manipulation of all this information can be done in an atomic way.
I am trying to expand the Meteor.users collection and I've got everything working fairly well so far except for some reason with this it's not allowing me to register new users anymore. I am using the accounts-google package for login/registration, logging in with an account that's already been created works great but when I attempt to register with a new account it doesn't work and I receive the following error in my browser console:
Exception in delivering result of invoking 'login': ReferenceError: ServiceConfiguration is not defined
at http://localhost:3000/packages/useraccounts_core.js?e3a764dbf634d8bf2a393797c0a82e9fadef2e7a:2551:48
at Accounts.callLoginMethod.userCallback (http://localhost:3000/packages/accounts-oauth.js?8a30b216f87b515ab9b4bf5d4970a7113d0c6c2f:163:7)
at http://localhost:3000/packages/accounts-base.js?7dabd814506e384c709f8bf707377955f9814129:612:26
at http://localhost:3000/packages/underscore.js?46eaedbdeb6e71c82af1b16f51c7da4127d6f285:794:19
at loggedInAndDataReadyCallback (http://localhost:3000/packages/accounts-base.js?7dabd814506e384c709f8bf707377955f9814129:708:7)
at null._callback (http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:999:22)
at _.extend._maybeInvokeCallback (http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3500:12)
at _.extend.receiveResult (http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3520:10)
at _.extend._livedata_result (http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:4625:9)
at onMessage (http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3365:12)
Can anyone see where I went wrong here? I've been playing around with it for quite a while and just cant seem to get it to work. Any help/insight is greatly appreciated.
Schema = {};
Schema.UserProfile = new SimpleSchema({
userProfile: {
type: Object
},
'userProfile.firstName': {
type: String,
optional: true,
label: "First Name"
},
'userProfile.lastName': {
type: String,
optional: true,
label: "Last Name"
},
'userProfile.birthday': {
type: Date,
optional: true,
label: "Date of Birth"
},
'userProfile.contactEmail': {
type: String,
optional: true,
label: "Contact Email"
},
'userProfile.gender': {
type: String,
allowedValues: ['Male', 'Female'],
optional: true,
label: "Gender"
},
'userProfile.country': {
type: String,
optional: true,
label: "Country"
},
'userProfile.address': {
type: String,
optional: true,
label: "Address"
},
'userProfile.city': {
type: String,
optional: true,
label: "City"
},
'userProfile.stateProvince': {
type: String,
optional: true,
label: "State/Province"
},
'userProfile.postalCode': {
type: String,
optional: true,
label: "Postal Code"
},
'userProfile.phoneNumber': {
type: String,
optional: true,
label: "Phone Number"
},
});
Schema.User = new SimpleSchema({
username: {
type: String,
// For accounts-password, either emails or username is required, but not both. It is OK to make this
// optional here because the accounts-password package does its own validation.
// Third-party login packages may not require either. Adjust this schema as necessary for your usage.
optional: true
},
emails: {
type: Array,
// For accounts-password, either emails or username is required, but not both. It is OK to make this
// optional here because the accounts-password package does its own validation.
// Third-party login packages may not require either. Adjust this schema as necessary for your usage.
optional: true
},
"emails.$": {
type: Object
},
"emails.$.address": {
type: String,
regEx: SimpleSchema.RegEx.Email
},
"emails.$.verified": {
type: Boolean
},
createdAt: {
type: Date
},
profile: {
type: Schema.UserProfile,
optional: true
},
// Make sure this services field is in your schema if you're using any of the accounts packages
services: {
type: Object,
optional: true,
blackbox: true
},
// Add `roles` to your schema if you use the meteor-roles package.
// Option 1: Object type
// If you specify that type as Object, you must also specify the
// `Roles.GLOBAL_GROUP` group whenever you add a user to a role.
// Example:
// Roles.addUsersToRoles(userId, ["admin"], Roles.GLOBAL_GROUP);
// You can't mix and match adding with and without a group since
// you will fail validation in some cases.
roles: {
type: Object,
optional: true,
blackbox: true
},
// In order to avoid an 'Exception in setInterval callback' from Meteor
heartbeat: {
type: Date,
optional: true
}
});
Meteor.users.attachSchema(Schema.User);
Meteor.users.allow({
insert: function(userId, doc) {
// only allow posting if you are logged in
console.log("doc: " + doc + " userId: " + userId);
return !! userId;
},
update: function(userId, doc, fieldNames) {
// only allow updating if you are logged in
console.log("doc: " + doc + " userId: " + userId);
// a user can only update his own user doc and only the 'userProfile' field
return !! userId && userId === doc._id && _.isEmpty(_.difference(fieldNames, ['userProfile']));
},
});
Your standard 'new' user object does not match your schema and will fail on creation.
So, to get around this you want to reformat the output of the object provided by the Account.onCreateUser function:
Accounts.onCreateUser(function (options, user) {
//format the 'user' object as you need it to be here
// to pass your schema validation
return user;
})