here's my photoSchema. it has a dbEntry object. should I create another dbEntry schema and ref it in the photoSchema. Or whatever I have is good enough? I am new to mongodb, try to figure out a correct way to create schema.
var photoSchema = new mongoose.Schema({
userId: ObjectId,
type: String,
createdOn: {type: Date, default: Date.now},
isDeleted: {type: Boolean, default: false},
isDownloaded: {type: Boolean, default: false},
dbFile: String,
dbEntry: {
revision: Number,
rev: String,
thumb_exists: Boolean,
bytes: Number,
modified: Date,
client_mtime: Date,
path: { type: String, unique: true}
}
});
It depends on how you plan to access the data. If you want to get the dbEntry object each time you query a photoSchema document, then what you have is probably the way to go.
If however, you're going to use dbEntry independent of the photoSchema document, then you should split it up and just keep a ref to it.
You can always fetch the ref using mongoose "populate"
Related
This is my Product table schema
let schema = new mongoose.Schema({
title: {type: String, required: true},
price: {type: Number, required: true},
description: {type: String, required: true},
sizes: {type: Object, required: true},
offer: {type: Number, default: 0},
images: {type: Array, required: true},
deal: {type: Boolean, default: false},
category: {
_id: {type: Schema.Types.ObjectId, required: true},
name: {type: String, required: true}
},
company_name: {type: String, required: true}
});
What I am trying to do
I am trying to validate if category.name value equal exist in my another table called Category.
You could use an async validator and query the categories collection. Something like this (using promise sytax for validator):
let schema = new mongoose.Schema({
title: {type: String, required: true},
price: {type: Number, required: true},
description: {type: String, required: true},
sizes: {type: Object, required: true},
offer: {type: Number, default: 0},
images: {type: Array, required: true},
deal: {type: Boolean, default: false},
category: {
_id: {type: Schema.Types.ObjectId, required: true},
name: {
type: String,
required: true,
validate: function(nameVal) {
return new Promise(function(resolve, reject) {
let Category = mongoose.model('Category'); //you would have to be sure that Category model is loaded before this one. One reason not to do this
Category.findOne({name: nameVal}, (err, cat) => resolve(cat ? true : false)); //any non null value means the category was in the categories collection
});
}
}
},
company_name: {type: String, required: true}
});
Some thoughts about this:
This is documented at http://mongoosejs.com/docs/validation.html#async-custom-validators.
As it states there, the validator is not run by default on updates. There are a lot of caveats to read through there.
In my experience with NoSQL DBs, the code creating a new Product would take care to make sure the category being assigned was valid. The code probably found the category from the DB at some point prior. So having a validator lookup would be redundant. But you may have a situation where you have a lot of code that creates Products and want validation in one place.
When I see that you are storing a document of {_id: ..., name: ...} as the category field in your Product schema, I think you might want this instead:
...
category: {Schema.Types.ObjectId, ref: 'Category'},
This allows you to store a reference to a category you have retrieved from the categories collection. Mongoose will load up the category document inline for you when you retrieve the products, if you use the populate method on your query. See http://mongoosejs.com/docs/populate.html. There are a lot of options with the populate functionality you might find useful. It does not do validation that the category is valid on save, as far as I know. But if you take this approach, you would have already looked up the category previously in the code before your save (see the link to get a better idea what I mean). Essentially this gives you join like behavior with MongoDB, with the storage savings and other benefits one expects from "normalization".
const UserSchema = new mongoose.Schema({
username: {type: String, unique:true},
fullname: {type: String, unique:false,default:''},
email: {type: String, unique:true},
password: {type: String, unique:false, default: ''},
userImage: {type: String, default:'default.png'},
facebook: {type: String, default: ''},
fbTokens: Array,
google: {type: String, default:''}
}, {timestamps: true});
This is my User schema. If i delete the indexes that are created on email,password,username and fullname and restart my express app using mongoose, it recreates these db indices. How to prevent this from happening ?
If you don't want the indexes on those fields then simply remove the unique:true/false parts from your schema definition. Mind you, if you need to enforce true uniqueness across your collection then this will always need to be done through unique indexes on the db (MongoDB) level.
The documentation states:
unique: {Boolean} - Tells Mongoose to ensure a unique index is created
for this path.
I know that if the size of the document grows, it will have a bad influence on Mongolia. So I avoid the following embedding document method.
However, many of the bulletin board basic examples use the following code.
var boardSchema = mongoose.Schema({
writer: String,
password: String,
title: String,
contents: String,
comments: [{
name: String,
memo: String,
date: {type: Date, default: Date.now}
}],
count: {type:Number, default: 0},
date: {type: Date, default: Date.now},
updated: [{contents: String, date:{type: Date, default: Date.now}}],
deleted: {type: Boolean, default: false}
});
In the case of bulletin boards and comments, is it possible to simply implement the embedded document method? Or did I misunderstand?
In addition, I implemented bulletin boards and comments in the following way.
var boardSchema = mongoose.Schema({
...
comments: [{type: ObjectId}],
...
});
var commentSchema = mongoose.Schema({
name: String,
memo: String,
date: Date
});
When creating in the comment schema, I implemented it by pushing the objectId as the bulletin board schema.
Issue:
I am creating an accounts table with the schema as shown below. I have just one account document for a user in a mongo server that's running in my localhost. After defining the schema and adding a single account document, I wrote an endpoint to query and fetch that document (Model.find(id: '12345')) using the id key. The resulting JSON document has all the data except the subdocument named accRef as show in the Schema below. When I try the same find query using the Mongo Shell, I get the results perfectly. I've been trying to find what's causing this for almost an entire day now, without making much headway. Any help would be highly appreciated.
I started with an empty schema and added each field one by one, starting with the id attribute and fire the find query. Every single time, I get all the 75+ elements and subdocuments, but the moment I add the accRef subdoc to my schema, this piece of info disappears from the result.
Is there a limitation to the number of subdocuments that can be defined in mongoose's schema?
Also, I cannot seem to insert/save any new account object using the same schema as shown below. However, I can perform this insert/save operation using the very same JSON object, using a client like MongoChef.
Here's my JS module where I've defined my schema:
import mongoose from 'mongoose';
mongoose.connect('mongodb://localhost:27017/accounts');
const Schema = mongoose.Schema;
const accountsSchema = new Schema({
_id: Schema.ObjectId,
id: String,
uuid: String,
firstName: String,
lastName: String,
contactNumber: Number,
dob: Date,
email: String,
originalEmail: {type: String, index: true},
password: String,
socialMedia: {
facebook: {
id: String,
email: String,
token: String
}
},
domainInfo: {
country: String,
currency: String,
lang: String,
domain: String
},
profile: {
type: Number,
id: Number,
points: Number,
accumulated: Number
},
perks: {
id: Number,
type: Number
},
address: {
address: String,
postalCode: String
},
paymentInfo: {
cardNumber: String,
isValidCard: Number,
paypal: {
email: String
},
bank: {
bankName: String,
bankNumber: String,
accontId: Number,
accountHolderName: String,
bankBranchCode: String,
paymentTypeId: Number
},
dateCreated: {type: Date, default: Date.now},
dateModified: {type: Date, default: Date.now},
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now}
},
metadata: {
timezone: String,
ipAddress: String,
signupMeta: String,
clientUserAgent: String,
signupUrl: String
},
accRef: {
type: String,
code: String,
url: String
},
key: String,
parentId: String,
type: String,
utm: String,
bounced: Number,
status: Number,
isPoints: Number,
isAdmin: String,
dateTutorialCompleted: {type: Date, default: Date.now},
dateCreated: {type: Date, default: Date.now},
dateModified: {type: Date, default: Date.now},
dateAccessed: {type: Date, default: Date.now},
dateVerified: {type: Date, default: Date.now},
datePurchased: Date,
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now}
});
const Account = mongoose.model('Account', accountsSchema);
export default Account;
The function in the service class that fires the mongo query:
import Account from '../../models/mongo';
const getAccountById = (accountId) => {
return Account.find({id: accountId})
.then(response => {
if(response.length === 0) {
throw new Error("Account Not Found");
}
console.log(response);
return response;
})
.catch(error => {
throw new Error(error.message);
});
};
Mongoose Version tried with: 4.7.7 and 4.7.8 (tried both)
MongoDB Version tried with: 3.2 and 3.4 (tried both)
Node Version: 7.4.0
Because I cannot edit properties of a non-lean mongoose result, I've used the result.toObject() statement, but that also means I cannot use the methods defined on my Schema.
Example
// Defining the schema and document methods
const User = new Schema({
email: {type: String, required: true, unique: true},
firstname: {type: String, required: true},
registration_date: {type: Date, default: Date.now, required: true},
insert: {type: String},
lastname: {type: String, required: true}
});
User.methods.whatIsYourFirstName = function () {
return `Hello, my firstname is:${this.firstname}`;
};
After a find:
user = user.toObject();
user.registration_date = moment(user.registration_date);
user.whatIsYourFirstName();
// result in "user.whatIsYourFirstName is not a function"
Is this solvable?
Methods and Models are part of Mongoose, not MongoDB.
Whenever you are calling .toObject() you are being returned an object which is ready for storage in MongoDB.
If you do need to do any sort of value transformation, I'd do it just before you deliver the value to the user. Being a time formatting, if you are building an API, I'd do that in the client; if you are working with templates try transforming the value on the same template.