How do you use Mongoose without defining a schema? - mongodb

In previous versions of Mongoose (for node.js) there was an option to use it without defining a schema
var collection = mongoose.noSchema(db, "User");
But in the current version the "noSchema" function has been removed. My schemas are likely to change often and really don't fit in with a defined schema so is there a new way to use schema-less models in mongoose?

I think this is what are you looking for Mongoose Strict
option: strict
The strict option, (enabled by default), ensures that values added to our model instance that were not specified in our schema do not get saved to the db.
Note: Do not set to false unless you have good reason.
var thingSchema = new Schema({..}, { strict: false });
var Thing = mongoose.model('Thing', thingSchema);
var thing = new Thing({ iAmNotInTheSchema: true });
thing.save() // iAmNotInTheSchema is now saved to the db!!

Actually "Mixed" (Schema.Types.Mixed) mode appears to do exactly that in Mongoose...
it accepts a schema-less, freeform JS object - so whatever you can throw at it. It seems you have to trigger saves on that object manually afterwards, but it seems like a fair tradeoff.
Mixed
An "anything goes" SchemaType, its flexibility comes at a trade-off of
it being harder to maintain. Mixed is available either through
Schema.Types.Mixed or by passing an empty object literal. The
following are equivalent:
var Any = new Schema({ any: {} });
var Any = new Schema({ any: Schema.Types.Mixed });
Since it is a schema-less type, you can change the value to anything
else you like, but Mongoose loses the ability to auto detect and save
those changes. To "tell" Mongoose that the value of a Mixed type has
changed, call the .markModified(path) method of the document passing
the path to the Mixed type you just changed.
person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save(); // anything will now get saved
Mongoose Schema Types

Hey Chris, take a look at Mongous. I was having the same issue with mongoose, as my Schemas change extremely frequently right now in development. Mongous allowed me to have the simplicity of Mongoose, while being able to loosely define and change my 'schemas'. I chose to simply build out standard JavaScript objects and store them in the database like so
function User(user){
this.name = user.name
, this.age = user.age
}
app.post('save/user', function(req,res,next){
var u = new User(req.body)
db('mydb.users').save(u)
res.send(200)
// that's it! You've saved a user
});
Far more simple than Mongoose, although I do believe you miss out on some cool middleware stuff like "pre". I didn't need any of that though. Hope this helps!!!

Here is the details description: [https://www.meanstack.site/2020/01/save-data-to-mongodb-without-defining.html][1]
const express = require('express')()
const mongoose = require('mongoose')
const bodyParser = require('body-parser')
const Schema = mongoose.Schema
express.post('/', async (req, res) => {
// strict false will allow you to save document which is coming from the req.body
const testCollectionSchema = new Schema({}, { strict: false })
const TestCollection = mongoose.model('test_collection', testCollectionSchema)
let body = req.body
const testCollectionData = new TestCollection(body)
await testCollectionData.save()
return res.send({
"msg": "Data Saved Successfully"
})
})
[1]: https://www.meanstack.site/2020/01/save-data-to-mongodb-without-defining.html

Note: The { strict: false } parameter will work for both create and update.

Its not possible anymore.
You can use Mongoose with the collections that have schema and the node driver or another mongo module for those schemaless ones.
https://groups.google.com/forum/#!msg/mongoose-orm/Bj9KTjI0NAQ/qSojYmoDwDYJ

Related

Confused about Types.ObjectId and Schema.Types.ObjectId from Mongoose

I'm trying to created nested schema with mongoose, attaching a user_id field for subdocuments. I found out this approach
const {Schema} = require("mongoose");
var user_id = {
type:Schema.Types.ObjectId,
ref:"User",
},
but later i found that Types can be imported from mongoose on top level , but the definition of the ObjectId is very different from Schema.Types, as such
const {Types} = require("mongoose");
var user_id = {
type:new Types.ObjectId(),
ref:"User",
},
I couldn't find documentation on this nuance....I guess i should use a consistent approach across the app, so can someone help to explain?
*Edit: I think the one I should use should be consistent with the way _id is defined for the User table, but which one was it used?
oh i got it.
newUser._id instanceof Types.ObjectId == true
so Types.ObjectId is the correct one to use.

Mongoose: Delete Item - First One always gets deleted

I am completely new to the fields of Mongoose and MongoDB.
I am currently trying trying to remove one element from my database.
This is my code so far:
My issueModel:
var mongoose = require('mongoose'); // loading module for mongoose
var db = mongoose.connect('mongodb://localhost/issuedb');
var issueSchema = new mongoose.Schema({
title: String,
description: String,
priority: String,
status: String
});
// Constructor Function:
var issueModel = mongoose.model('issues', issueSchema); // have to give the
name of the collection where the element should be stored + Schema
// Export this Construction Function for this Module:
module.exports = issueModel; // careful: module != model !
My post method for using the delete method:
// creating the router for deleting one item:
router.post('/delete/:id', (req, res) => {
console.log(req.params.id);
issueModel.remove({id: req.params.ObjectId})
.setOptions({ single: true }).exec(function (err, deleted) {})
.then(issues => res.render('issue', {issues: issues}));
The thing i would like to do here is using the object id - which is correctly stored in req.params.ObjectID according to my console.log, and deleting the corresponding object.
But currently , when i have got a table with about 3-4 entries, always the first one gets deleted. Why is that? I am really TOTALLY new and really tried searching a lot, but i could not find any solution until now. I am happy about any tips that would help me.
What am i doing wrong?
The ID in the URL and the Object.ID are the same! Why is the first object deleted then, not the second or the third?
I am hopeless right now.
I also read about the remove() option not being really used in todays time. But we were told at university to use this method right now.
I also tried findOneByID and delete methods i found in the mongoose database.
If you need any more code please let me know!
You can use one of the convenience methods for this: findByIdAndRemove:
issueModel.findByIdAndRemove(req.params.ObjectId, function(err) {
if (err) { ... failed }
});
This will remove a whole document matching the ID which I think its what you want, if you want to a remove property from a document that's a different query.
If you don't use one of the convenience methods which just take IDs (have ById in them), then you have to convert your ID from a string to an ObjectId:
const { ObjectId } = require('mongodb');
issueModel.remove({ id: ObjectId(req.params.ObjectId) }).setOptions({ single: true })

Mongoose Querying Views

I'm currently using mongoose v. 5.25, against mongoDB v.3.6.
My application is supposed to query data from many different views, for instance, a view I currently have at my DB: db.joboffers_view.find()
will return many records that have been aggregated from different collections.
For a normal collection model, I query it like so:
const model = db.model(attribute);
/*where attribute, can be any registered schema */
model.find().
then((result) => {
resolve(result);
}).
catch((err) => {
reject(err);
});
Then way I register my models is something like this (simplified code):
//...
//abstracting boring connection methods
const db = mongoose.connection
//...
//simple model schema
const users_schema = {
_id: ObjectId,
another_field: String
};
//here I'm registering a schema for a VIEW, instead of normal collection
const view_schema = {
_id: ObjectId,
another_field: String
};
//...
//then
db.model('users', users_schema);
db.model('view', view_schema);
When I run a query from any of my registered models, I get the results just fine. However, when I run it against a model that represents a view on my mongo database, it returns an empty array.
No errors, no nothing, just an empty array.
I have looked through mongoose documentation, and I didn't find any specific method or pattern for querying a view, instead of a collection data.
It seems to be the same way I would do for any other collection I have in my system.
Am I missing something?
I also faced the same issue and figured out the problem is that mongoose, by default, reads collection names by pluralizing the model/view name.
So when you create any view and want to use it in mongoose, either make sure your view name is plural (add s to end of view name) or pass a collection name when initializing a schema.
Example
const users_schema = {
_id: ObjectId,
another_field: String
};
mongoose.model('vw_user_info', users_schema, 'vw_user_info');
I have same problem, but i solved it, please check the name of the view in mongodb, it must be same with db.model('view_name', view_schema);
You can open Mongoose debug by config like this mongoose.set('debug', true);
Add 3rd argument
db.model('view', view_schema, 'view_name_in_db')

How to find string in array using Mongoose?

I have a schema through mongoose:
const mongoose = require('mongoose');
const recipeSchema = mongoose.Schema({
title: String,
chef: String,
updated: {type: Date, default: Date.now},
region: String,
ingredients: [String],
instructions: [String]
}, { collection: 'recipes' })
module.exports = mongoose.model('Recipes', recipeSchema);
I find the mongoose docs really difficult to understand. I am trying to search for a match of all substring within the 'ingredients' array. I read somewhere that it could be done like so:
.find({ingredients: 'ing1'}) // not working
.find({'ing1': {$in: ingredients}}) // not working
I find it pretty difficult to find in depth tutorials on mongoose as well. Im thinking about not using it at all anymore and just sticking to mongodb shell.
You can use a regex search to match substrings:
.find({ingredients: /ing1/})
The reason that you use mongoose is for testability.
Instead of having to work with a MongoDb instance, which, in Windows can be a pain with the .lock file and the service, mongoose creates the schema that you can test your code with.
The mongoose way is ideal for TDD/TFD.
Below is the model and the mocha test:
recipemodel.js
var mongoose = require('mongoose'),Schema=mongoose.Schema;
var RecipeSchema = new mongoose.Schema({});
RecipeSchema.statics.create = function (params, callback) {
'\\ params is any schema that you pass from the test below
var recipe = new RecipeSchema(params);
recipe.save(function(err, result) {
callback(err, result);
});
return recipe;
};
var recipemodel=mongoose.model('Model', RecipeSchema);
module.exports = recipemodel;
You don't need to describe the schema, mongoose will create it for you when you pass the values of the collection from a mocha test, for example!
The mocha test is below:
var mongooseMock = require('mongoose-mock'),
proxyquire = require('proxyquire'),
chai = require('chai'),
expect = chai.expect,
sinon = require('sinon'),
sinonChai = require("sinon-chai");
chai.use(sinonChai);
describe('Mocksaving a recipe ingredient', function () {
var Recipe;
beforeEach(function () {
Recipe = proxyquire('./recipemodel', {'mongoose': mongooseMock});
});
it('checks if ingredient '+'ing1' + ' saved to mongoose schema', function
(done) {
var callback = sinon.spy();
var recipe = Recipe.create({ title: "faasos", chef:
'faasos',region:'Chennai',ingredients:'ing1',instructions:'abc' },
callback);
expect(recipe.save).calledOnce;
expect(recipe.ingredients).equals('ing341');
done();
});
});
The call to a sinon spy is simply to ensure that the call to the data in the schema got saved (mock saved!) and that the 'save' method did get called at least once. This logic flow is in sync with your actual logic, as you would use in code, when the save on a mongodb collection would be made.
Simply change the value to 'ing1' to make the test pass when you run the test.
For an array type, pass the values as below:
var recipe = Recipe.create({ title: "faasos", chef:
'faasos',region:'Chennai',ingredients:'ing341,ing1',instructions:'abc' }, callback);
expect(recipe.save).calledOnce;
expect(recipe.ingredients).to.include('ing1');
Try this:
.ingredients.find((i) => i === "ing1")
for all elements in the ingredients array, it looks if the content, here a string element, is strictly equal to "ing1"

Embedded document without Array?

I understand how to embed documents in Mongoose, and they seem fairly straightforward if storing as arrays, for which the use case is fairly obvious:
var CommentSchema = new Mongoose.Schema({...});
var BlogPostSchema = new Mongoose.Schema({
comments : [CommentSchema],
});
But, what I don't see how to do after looking over the documentation forward and backward, is how to store a single sub-document that doesn't need or want to be in an Array.
var UserSchema = new Mongoose.Schema({...});
var BlogPostSchema = new Mongoose.Schema({
author: ??? // 'UserSchema' and UserSchema do not work here.
});
Is there any way to make this work? I don't want to just store the ObjectId, but rather, store a complete copy of the User record, but don't need or want an array.
You cannot embed schemas in this way, with the reasoning that those child docs would be confused with full documents, see this bug thread, where it is stated:
the reason we haven't added this support in the past is b/c this leaves us wondering if all pre-hooks will be executed the same way for the pseudo-child document as well as it implies that we can call save() on that child.
The answer here is to share not the schema, but just the definition.
var userdef = { name: String };
var UserSchema = new Schema(userdef);
var BlogPostSchema = new Schema({author: userdef});
This would result in a nested user object, without actually nesting the Schema.
Just sharing information doesn't support validation bubbling. And you may need validation of UserSchema also.
Instead I recommend array length validation
author: {type:[UserSchema], validate: function (arr) { return arr.length == 1 }},
UPDATE:
In case anyone comes across this now, as of Mongoose 4.2.0 single embedded subdocuments exist! :)
http://mongoosejs.com/docs/subdocs.html#single-embedded