array schema single value mongodb using node js - javascript

I am trying to fetch single value from my level collection using this level schema
After new Schema I use ( [] ) I don't know exactly the use of this. Why we use this. But my project requirement is to use this []
var LevelSchema = new Schema(
[
{
_id: { type: String },
age: { type: Number },
level_num: { type: Number },
min_score: { type: Number },
max_questions: { type: Number }
}
]);
So when I am trying to fetch the value using this node js
this.levelChange = function(req, res, next){
try{
var query = {level_num:1};
QuizLevels.find(query,function(err,data){
if(err){
return next(err);
}else if(data.min_score===10){
console.log(data.min_score);
}else{
console.log("Error");
}
});
}catch(err){
console.log(err);
}
};
I proper send all level information stored in my MongoDB to the browser. But when I am trying to fetch a single value field
min_score from the database and trying to send it in the browser or in the console it shows undefined. Means value cannot parse properly from MongoDB
to browser or console using this node js code.
so there are any further steps before to fetch this array schema.So that single value field parses properly to console
3). This is my JSON data
I am not sure that json data is exact according to level schema that i used to fetch.
{
"age":5,
"level_num":1,
"min_score":10,
"max_questions":30
}
{
"age":5,
"level_num":2,
"min_score":12,
"max_questions":33
}

Related

Mongodb using dynamic fields with $not

I programming in react, mongodb, nodejs and expressjs. I have a problem that I cannot solve. I would like to use dynamic fields from $not on the server. For example, the server gets the column name from the front and it is supposed to return the number of documents where the text is different from an empty string, i.e. ''. I've tried to do something like this(code below), but it doesn't help.
const query = {};
query[type] = { $not: '' };
User.countDocuments(query, (err, data) => {
if (err) return res.json({ success: false, error: err });
return res.json({ success: true, data: data });
});
You are close, you probably were looking for $ne instead of $not. So changing it to
const query = {};
query[type] = { $ne: '' };
should fix the issue. This would find all documents where the dynamic type field does not equal ''. If you want to do the inverse, i.e. find all documents where the dynamic field equals an empty string, change it to:
query[type] = { $eq: '' };

MongoDB can't find records with specific number for variable

When I try find records with specific value for one variable in MongoDB directly, it is found with no problem.
{defCordX: 15}
https://puu.sh/GV5jr/e3362cacfd.png
But when I do the same filter in my express app, it returns empty array
const findReportsVillage = Reports
.find({defCordX: 15})
findReportsVillage.exec((err, data) => {
console.log(data);
res.render('index', { title: 'Reports', data, searchVillage });
});
Can someone tell me why this is happen?

How to make mongo validate documents before insert or update in Vertx?

I'm newbie using Vertx, I'm building a basic Api Rest with Vertx + Mongo using Javascript.
I'm looking for some way to automaticly validate the incoming documents before insert or update (for example, something like schemas in Mongoose).
I got the following:
POST entry point Inserting a new cat
var BodyHandler = require("vertx-web-js/body_handler");
var Router = require("vertx-web-js/router");
var router = Router.router(vertx);
router.post("/cat")
.produces("application/json")
.handler(BodyHandler.create().handle)
.handler(controller.createCat);
Controller's function Inserting a new cat
createCat: function (ctx) {
var response = ctx.response();
var body = ctx.getBodyAsJson() || {};
console.log('inserting cat')
connection.mongoClient.insert("cats", { name: body.name }, function (res, res_err) {
if (res_err == null) {
var id = res;
console.log("Inserted cat with id " + id);
response.putHeader("content-type", "application/json");
response.end(JSON.stringify(id));
} else {
console.log('err')
res_err.printStackTrace();
}
});
}
The problem is that I can insert empty documents resulting documents with only one field: the mongo ID.
TL;DR - I want to know if there is some way to tell Mongo that name field is ALWAYS required.
Solution I used:
mongoClient.runCommand("collMod", { collMod: "cats", validator: { $and: [ {"name": {$type: "string", $exists: true}} ] }, validationLevel: "strict", validationAction: "error" }, function(res, res_err) {
if (res_err) res_err.printStackTrace()
else console.log('The schema of cats collection has been updated')
});
In mongodb there is a concept of validation with query filters for collection: https://docs.mongodb.com/manual/core/schema-validation/#query-expressions
If the "name" field is always required, you can create your collection like this:
db.createCollection("cats", {
validator: {
$and: [ {"name": {$type: "string", $exists: true}} ]
})
you can create your own schema in a separate json file, then create your own validation method when the context is provided by ur router.
I assume different endpoint has different validation.
you can also create a middleware using gateleen
you have plenty of options, mongoose its benefits but the whole point of using mongodb is not creating a schema. Therefore, u can assume that the request should have specific mandatory fields to validate.

Update Array attribute using Mongoose

I am working on a MEAN stack application in which i defined a model using following schema:
var mappingSchema = new mongoose.Schema({
MainName: String,
Addr: String,
Mapping1: [Schema1],
Mappings2: [Schema2]
},
{collection : 'Mappings'}
);
I am displaying all this data on UI and Mapping1 & Mapping2 are displayed in the 2 tables where I can edit the values. What I am trying to do is once I update the values in table I should update them in database. I wrote put() api where I am getting these two updated mappings in the form of object but not able to update it in database. I tried using findAndModify() & findOneAndUpdate() but failed.
Here are the Schema1 & Schema2:
const Schema1 = new mongoose.Schema({
Name: String,
Variable: String
});
const Schema2 = new mongoose.Schema({
SName: String,
Provider: String
});
and my put api:
.put(function(req, res){
var query = {MainName: req.params.mainname};
var mapp = {Mapping1: req.params.mapping1, Mapping2: req.params.mapping2};
Mappings.findOneAndUpdate(
query,
{$set:mapp},
{},
function(err, object) {
if (err){
console.warn(err.message); // returns error if no matching object found
}else{
console.log(object);
}
});
});
Please suggest the best to way update those two arrays.
UPDATE :
I tried this
var mapp = {'Mapping2': req.params.mapping2};
Mappings.update( query ,
mapp ,
{ },
function (err, object) {
if (err || !object) {
console.log(err);
res.json({
status: 400,
message: "Unable to update" + err
});
} else {
return res.json(object);
}
});
what I got is
My array with size 3 is saved as String in Mapping2 array.
Please help. Stuck badly. :(
From Mongoose's documentation I believe there's no need to use $set. Just pass an object with the properties to update :
Mappings.findOneAndUpdate(
query,
mapp, // Object containing the keys to update
function(err, object) {...}
);

Mongoose find/update subdocument

I have the following schemas for the document Folder:
var permissionSchema = new Schema({
role: { type: String },
create_folders: { type: Boolean },
create_contents: { type: Boolean }
});
var folderSchema = new Schema({
name: { type: string },
permissions: [ permissionSchema ]
});
So, for each Page I can have many permissions. In my CMS there's a panel where I list all the folders and their permissions. The admin can edit a single permission and save it.
I could easily save the whole Folder document with its permissions array, where only one permission was modified. But I don't want to save all the document (the real schema has much more fields) so I did this:
savePermission: function (folderId, permission, callback) {
Folder.findOne({ _id: folderId }, function (err, data) {
var perm = _.findWhere(data.permissions, { _id: permission._id });
_.extend(perm, permission);
data.markModified("permissions");
data.save(callback);
});
}
but the problem is that perm is always undefined! I tried to "statically" fetch the permission in this way:
var perm = data.permissions[0];
and it works great, so the problem is that Underscore library is not able to query the permissions array. So I guess that there's a better (and workgin) way to get the subdocument of a fetched document.
Any idea?
P.S.: I solved checking each item in the data.permission array using a "for" loop and checking data.permissions[i]._id == permission._id but I'd like a smarter solution, I know there's one!
So as you note, the default in mongoose is that when you "embed" data in an array like this you get an _id value for each array entry as part of it's own sub-document properties. You can actually use this value in order to determine the index of the item which you intend to update. The MongoDB way of doing this is the positional $ operator variable, which holds the "matched" position in the array:
Folder.findOneAndUpdate(
{ "_id": folderId, "permissions._id": permission._id },
{
"$set": {
"permissions.$": permission
}
},
function(err,doc) {
}
);
That .findOneAndUpdate() method will return the modified document or otherwise you can just use .update() as a method if you don't need the document returned. The main parts are "matching" the element of the array to update and "identifying" that match with the positional $ as mentioned earlier.
Then of course you are using the $set operator so that only the elements you specify are actually sent "over the wire" to the server. You can take this further with "dot notation" and just specify the elements you actually want to update. As in:
Folder.findOneAndUpdate(
{ "_id": folderId, "permissions._id": permission._id },
{
"$set": {
"permissions.$.role": permission.role
}
},
function(err,doc) {
}
);
So this is the flexibility that MongoDB provides, where you can be very "targeted" in how you actually update a document.
What this does do however is "bypass" any logic you might have built into your "mongoose" schema, such as "validation" or other "pre-save hooks". That is because the "optimal" way is a MongoDB "feature" and how it is designed. Mongoose itself tries to be a "convenience" wrapper over this logic. But if you are prepared to take some control yourself, then the updates can be made in the most optimal way.
So where possible to do so, keep your data "embedded" and don't use referenced models. It allows the atomic update of both "parent" and "child" items in simple updates where you don't need to worry about concurrency. Probably is one of the reasons you should have selected MongoDB in the first place.
In order to validate subdocuments when updating in Mongoose, you have to 'load' it as a Schema object, and then Mongoose will automatically trigger validation and hooks.
const userSchema = new mongoose.Schema({
// ...
addresses: [addressSchema],
});
If you have an array of subdocuments, you can fetch the desired one with the id() method provided by Mongoose. Then you can update its fields individually, or if you want to update multiple fields at once then use the set() method.
User.findById(userId)
.then((user) => {
const address = user.addresses.id(addressId); // returns a matching subdocument
address.set(req.body); // updates the address while keeping its schema
// address.zipCode = req.body.zipCode; // individual fields can be set directly
return user.save(); // saves document with subdocuments and triggers validation
})
.then((user) => {
res.send({ user });
})
.catch(e => res.status(400).send(e));
Note that you don't really need the userId to find the User document, you can get it by searching for the one that has an address subdocument that matches addressId as follows:
User.findOne({
'addresses._id': addressId,
})
// .then() ... the same as the example above
Remember that in MongoDB the subdocument is saved only when the parent document is saved.
Read more on the topic on the official documentation.
If you don't want separate collection, just embed the permissionSchema into the folderSchema.
var folderSchema = new Schema({
name: { type: string },
permissions: [ {
role: { type: String },
create_folders: { type: Boolean },
create_contents: { type: Boolean }
} ]
});
If you need separate collections, this is the best approach:
You could have a Permission model:
var mongoose = require('mongoose');
var PermissionSchema = new Schema({
role: { type: String },
create_folders: { type: Boolean },
create_contents: { type: Boolean }
});
module.exports = mongoose.model('Permission', PermissionSchema);
And a Folder model with a reference to the permission document.
You can reference another schema like this:
var mongoose = require('mongoose');
var FolderSchema = new Schema({
name: { type: string },
permissions: [ { type: mongoose.Schema.Types.ObjectId, ref: 'Permission' } ]
});
module.exports = mongoose.model('Folder', FolderSchema);
And then call Folder.findOne().populate('permissions') to ask mongoose to populate the field permissions.
Now, the following:
savePermission: function (folderId, permission, callback) {
Folder.findOne({ _id: folderId }).populate('permissions').exec(function (err, data) {
var perm = _.findWhere(data.permissions, { _id: permission._id });
_.extend(perm, permission);
data.markModified("permissions");
data.save(callback);
});
}
The perm field will not be undefined (if the permission._id is actually in the permissions array), since it's been populated by Mongoose.
just try
let doc = await Folder.findOneAndUpdate(
{ "_id": folderId, "permissions._id": permission._id },
{ "permissions.$": permission},
);

Categories

Resources