MongoDB w/ Mongoose - Where to put syntax to ensureIndex spanning multiple fields? - javascript

I'm trying to implement this solution and I'm not sure where to put it. I see the db variable called frequently, but I'm still new to node and mongoDb, so I don't know how to call it in my Model. Here is the syntax to ensure an index spanning multiple fields...
db.collection.ensureIndex( {
description: "text",
title: "text"
} );
Here is my model...
// Module dependencies.
var mongoose = require('mongoose'),
config = require('../../config/config'),
Schema = mongoose.Schema,
findOrCreate = require('mongoose-findorcreate'),
textSearch = require('mongoose-text-search');
// Product Schema
var ProductSchema = new Schema({
created: {
type: Date,
default: Date.now
},
retailer: {
type: String,
required: true,
trim: true
},
retailer_category: {
type: String,
required: true,
trim: true
},
product_id: {
type: String,
required: true,
trim: true
},
link: {
type: String,
trim: true
},
title: {
type: String,
trim: true
},
price: {
type: Number
},
// Rating - 0 out of 5 (can be decimal)
rating: {
type: Number
},
description: {
type: String,
trim: true
},
variations: {
type: Schema.Types.Mixed,
default: []
},
images: {
type: Boolean,
default: false
}
});
// Validations
ProductSchema.index({ retailer: 1, product_id: 1 }, { unique: true });
// Statics
ProductSchema.statics = {
load: function(id, cb) {
this.findOne({
_id: id
}).exec(cb);
}
};
// Plug-Ins
ProductSchema.plugin(findOrCreate);
ProductSchema.plugin(textSearch);
mongoose.model('Product', ProductSchema);

var Product = mongoose.model('Product', ProductSchema);
Product.ensureIndexes( function(err) {
if (err) {
console.log(err);
}
})
It's worth noting:
When your application starts up, Mongoose automatically calls ensureIndex for each defined index in your schema. While nice for development, it is recommended this behavior be disabled in production since index creation can cause a significant performance impact. Disable the behavior by setting the autoIndex option of your schema to false.
from http://mongoosejs.com/docs/guide.html

I scratched my head over this one too. After digging around the mongoose test cases, I found that ensureIndex resides in a mongoose model's collection property.
var ProductModel = mongoose.model('Product', ProductSchema);
ProductModel.collection.ensureIndex({
description : 'text',
title : 'text'
}, function(error, res) {
if(error){
return console.error('failed ensureIndex with error', error);
}
console.log('ensureIndex succeeded with response', res);
});
Note that a callback is required, or Mongo will throw the error:
Error: Cannot use a writeConcern without a provided callback

Related

Node.js + Mongoose: How to use a virtual property to associate an ObjectID with the property?

I'm trying to access a MongoDB database using Node.js and Mongoose.
I created a virtual property in Schema called username. See the code that follows.
const mongoose = require("mongoose");
const User = require("../models/user");
const datatypes = ['temperature', 'humidity'];
const nodeSchema = new mongoose.Schema(
{
MACAddress: {
type: String,
required: true,
trim: true,
uppercase: true,
match: /^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$/,
},
alias: {
type: String,
trim: true,
},
coordinates: {
type: String,
required: false,
match: /^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$/,
},
address: {
type: String,
required: false,
},
userID: {
type: mongoose.Types.ObjectId,
},
nodeType: {
type: String,
enum: ['router', 'node'],
default: 'node',
},
dataTypes: {
type: [String],
enum: datatypes,
required: true,
}
},
{
timestamps: true,
}
);
The virtual property is used to set the userID property. See the code that follows.
// virtual field
nodeSchema.virtual("username").set(async function (username) {
this.userID = await this.getUserID(username);
});
// methods
nodeSchema.methods = {
getUserID: function (username) {
if (!username) return null;
return User.find({username: username}).then(userDoc => userDoc[0]._id);
},
};
To add a new document to the database, I am using the following code.
const newNode = new Node(newNodeData);
newNode.save().then( (node) => {
console.log(node.userID)
}
)
The problem is this... Calling the User.find function returns a promise. Even using await (see previous code), newNode.save() saves the document in the database without the userID property.
If I change the code to the following snippet, which doesn't use promise, the userID property is saved in the database with no problem. However, this is not what I want.
// virtual field
nodeSchema.virtual("username").set(async function (username) {
let ObjectId = mongoose.Types.ObjectId;
this.userID = new ObjectId("6245e896afe465a25047302e");
});
How can I force newNode.save() to wait for the promise result before saving the document to the database?

findByIdAndUpdate not failing even when body contains params not in schema

Taken from This MERN tutorial...
I have a mongoose schema with 4 fields:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let Todo = new Schema({
name: {
type: String,
required: true
},
description: {
type: String,
required: false
},
comments: {
type: String,
required: false
},
done: {
type: Boolean,
required: true
},
});
module.exports = mongoose.model('Todo', Todo);
I'm calling this update route:
todoRoutes.route('/update/:id').post(function(req, res) {
Todo.findByIdAndUpdate(req.params.id, req.body, function(err, todo) {
if (err)
res.status(400).send('Updating item failed: ' + err);
else
todo.save().then(todo => {
res.json('Item updated!');
}).catch(err => {
res.status(400).send("Update not possible: " + err);
});
});
});
with the following body:
{
"name": "bla"
}
and gets an "ok" status, and the document is updated as I wanted. However, running the same update with an extra field, also gets an "ok" status, though it should have failed IMO:
{
"name": "bla",
"unwanted_field": true
}
The field is not shown when I GET the DB, but it still returns without any error. Why?
Why update does not require the "required" fields, and accepts any updates?
enter link description here has a strict option, that can be used like this:
let Todo = new Schema({
name: {
type: String,
required: true
},
description: {
type: String,
required: false
},
comments: {
type: String,
required: false
},
done: {
type: Boolean,
required: true
},
},
{
strict: true // This is true by default. Changing this to "throw" will throw an error, as shown in the image attached.
}
);
I add here an image of an error I got using postman, trying to add bad data after adding the strict: "throw" option:

Asynchronous Programming in node js to pass constants/predefined mandatory values through mongoose model

I have multiple questions, please go through my code.
1) how to pass constants/predefined mandatory values through model?
For eg. I have some fields which user must be passing the values and some constants to pass on inside the kafkaSchema.config[ ] and also livySchema.args[ ]. The code i want to pass through is in second question on the same question thread.
const mongoose = require('mongoose');
const livy_schema = mongoose.Schema({
file: { type: String, required: true },
name: { type: String, required: true },
className: { type: String, required: true },
args: [{ type: mongoose.Schema.Types.Mixed, required: true }] //here i have constants to pass on to
});
const kafka_schema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true, unique: false },
config: { type: mongoose.Schema.Types.Mixed, required: true } //here i have constants to pass on to
});
const enrichedEventSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
projectId: { type: mongoose.Schema.Types.ObjectId, ref: 'Project', required: true },
name: { type: String, required: true, unique: true },
description: { type: String, required: false },
type: { type: String, enum: ["Enriched"], required: true },
format: { type: String, enum: ["JSON", "DELIMITED", "FixedWidth", "LOG"], required: true },
kafka: [kafka_schema],
livy: [livy_schema] // how to make this schema required:true?
});
module.exports = mongoose.model('EnrichedEvent', enrichedEventSchema);
2) how to make this code to run asynchronously, Right now its working synchronously. For example, Its able to save the eventdata in event collection in database, then its updating the project collection, then calling axios.post method to call my livy server and kafka server in order. What i want to do is save the eventdata in event collection in database, then update the project collection (synchronously), meanwhile I want to call my livy and kafka server at the same time (Asynchronously).
router.post("/:projectId/events/enriched", (req, res, next) => {
const enrichedEvent = new EnrichedEvent({
_id: mongoose.Types.ObjectId(),
name: req.body.name,
projectId: req.params.projectId, //taking from url
description: req.body.description,
type: req.body.type,
format: req.body.format,
kafka: req.body.kafka,
livy: req.body.livy
});
enrichedEvent.save()
.then(result => {
console.log(result);
res.status(201).json({
message: "Event stored",
createdEvent: {
_id: result._id,
projectId: result.projectId,
name: result.name,
description: result.description,
type: result.type,
kafka: result.kafka,
livy: result.livy
}
});
Project.findOneAndUpdate({ _id: result.projectId },
{ $push: { enrichedEvents: result._id } })
axios.post("http://52.xxx.xxx.199:8998/batches", result.livy)
.then(function (response) {
console.log(response);
})
.then(axios.get("http://52.xxx.xxx.199:8998/batches/"), function (res) {
console.log(res);
})
axios.post("http://52.xxx.xxx.199:8083/connectors", result.kafka)
.then(function (response) {
console.log(response);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
});
Question may seem bit lengthy, but valid question to ask on SO. Please guide me to right direction.
1)
const enrichedEventSchema = mongoose.Schema({
// ...
livy: { type: [livy_schema], required: true }
});
2)
return enrichedEvent.save().
then(result => {
// ...
return Project.findOneAndUpdate(/*...*/);
}).
then(() => {
// ...
return Promise.all([axios.post(/*...*/), axios.post(/*...*/]);
});
Hey try the following:
1) for saving user's entered configurations and also having default constants. You could use mongoose pre save hook.
https://mongoosejs.com/docs/middleware.html#pre
livy_schema.pre('save', function(next) {
this.args = { ...this.args, ...CONSTANTS }; //I'm use es6's spread operator
next();
});
kafka_schema.pre('save', function(next) {
this.config = { ...this.config, ...CONSTANTS }; //I'm use es6's spread operator
next();
});
2) For second question: try following:
axios.all([
axios.post("http://52.221.178.199:8998/batches", result.livy),
axios.post("http://52.221.178.199:8083/connectors", result.kafka)
]);

".findOneAndUpdate()" not updating database properly (Mongodb & Node.js)

I try to use .findOneAndUpdate() to update my database.
No error message, but this part of the database is not updated with new data. The embedded document competitorAnalysisTextData is still empty.
// on routes that end in /users/competitorAnalysisTextData
// ----------------------------------------------------
router.route('/users/competitorAnalysisTextData/:userName')
// update the user info (accessed at PUT http://localhost:8080/api/users/competitorAnalysisTextData)
.post(function(req, res) {
console.log('1');
// Just give instruction to mongodb to find document, change it;
// then finally after mongodb is done, return the result/error as callback.
User.findOneAndUpdate(
{ userName : req.params.userName},
{
$set:
{ "competitorAnalysis.firstObservation" : req.body.firstObservation,
"competitorAnalysis.secondObservation" : req.body.secondObservation,
"competitorAnalysis.thirdObservation" : req.body.thirdObservation,
"competitorAnalysis.brandName" : req.body.brandName,
"competitorAnalysis.productCategory" : req.body.productCategory
}
},
{ upsert: true },
function(err, user) {
// after mongodb is done updating, you are receiving the updated file as callback
console.log('2');
// now you can send the error or updated file to client
if (err)
return res.send(err);
return res.json({ message: 'User updated!' });
});
})
Update
This is my "User" Schema part:
// grab the things we need
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// Require the crypto module for password hash
'use strict';
var crypto = require('crypto');
// create competitorAnalysisSchema
var CompetitorAnalysis = new Schema({
firstObservation: { type: String },
secondObservation: { type: String },
thirdObservation: { type: String },
brandName: { type: String },
productCategory: { type: String }
});
// create competitorAnalysisPhotoSchema
var CompetitorAnalysisPhoto = new Schema({
photo1: {type: String},
photo2: {type: String},
photo3: {type: String},
photo4: {type: String}
});
// create UserSchema
var UserSchema = new Schema({
userName: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
currentDemo: { type: String },
nextDemo: { type: String },
startTime: { type: String },
startLocation: { type: String },
arriveTime: { type: String },
arriveLocation: { type: String },
leaveTime: { type: String },
leaveLocation: { type: String },
competitorAnalysis: [CompetitorAnalysis],
competitorAnalysisPhoto: [CompetitorAnalysisPhoto],
created_at: Date,
updated_at: Date
});
// the schema is useless so far
// we need to create a model using it
var User = mongoose.model('User', UserSchema);
// make this available to our users in our Node applications
module.exports = User;
in javascript if you wish to update an object inside an array, you need to pick the index
var arr = [{name: "person1"},{name:"person2"}]
arr[0].name = "myname"
arr[1].name = "myFriend"
So it's the same in mongodb, check this link for detail example, or you can manually input the index, for quick hack.
User.findOneAndUpdate(
{ userName : req.params.userName},
{
$set:
{ "competitorAnalysis.0.firstObservation" : req.body.firstObservation,
"competitorAnalysis.0.secondObservation" : req.body.secondObservation,
"competitorAnalysis.0.thirdObservation" : req.body.thirdObservation,
"competitorAnalysis.0.brandName" : req.body.brandName,
"competitorAnalysis.0.productCategory" : req.body.productCategory
}
},
{ upsert: true },
function(err, user) {
// after mongodb is done updating, you are receiving the updated file as callback
console.log('2');
// now you can send the error or updated file to client
if (err)
return res.send(err);
return res.json({ message: 'User updated!' });
});
})
You should use the code above to update nested-array not to add to empty-array.
In javascript, if an array is still empty, we use .push() to add, while in mongodb the command is $push
var arr = []
arr.push({name:"person1"})

Adding JSON array to a Mongoose schema (JavaScript)

I'm creating an Android App (A baseball app) where I'm using MongoDB to store my data. the way I want my JSON data to be stored into the database is like this.
{"email#domain.com":{
"coachName": "Smith"
players:[
player1:{
"throws_":"n\/a",
"position":"position not set",
"number":"1",
"playerNum":"H8E83NxRo6",
"bats":"n\/a",
"team_name":"Team",
"name":"Name"}
player2:{
"throws_":"n\/a",
"position":"position not set",
"number":"1",
"playerNum":"H8E83NxRo6",
"bats":"n\/a",
"team_name":"Team",
"name":"Name"}
]
}
sorry if there is any syntax error, but essentially that is the layout i want for the JSON. Where the mongo page "id" is the persons email. and where "players" is an array of the list of players the coach has.
My question is, how can I
properly setup the Mongoose schema to look like this?
when the app sends the JSON data, how can I parse it to store the data?
and if possible (ill try and figure this part on my own if no one can) if multiple players are being added at once, how can i store them if there's already players in the array?
All of this is backend/server side, I have the android working properly, i just need help with storing it to look like this in JavaScript.
You dont want to use a dynamic variable as a field name. I'm talking about the email you have "email#domain.com". This wouldn't be good because how will you find the object. Its not common to search for object by there fields, you use the field name to describe what it is your looking for. You will need 2 models. One for player and one for coach. Coach refrences a Player in its Players array field.
If you format your JSON correctly you wont have to do any parsing, just make sure the JSON you are sending is correctly formatted.
Look at the addPlayer function.
Controller file (Answer for questions 2 and 3)
create = function(req, res) {
var coach = new Coach(req.body);
coach.user = req.user;
coach.save(function(err) {
if (err) {
return res.status(400).send({
// Put your error message here
});
} else {
res.json(coach);
}
});
};
read = function(req, res) {
res.json(req.coach);
};
addPlayer = function(req, res) {
var coach = req.coach;
console.log('updating coach', req.coach);
var player = new Player(req.newPlayer);
coach.players.push(newPlayer);
coach.save(function(err) {
if (err) {
return res.status(400).send({
// Put your error message here
});
} else {
res.json(coach);
}
});
};
Player
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Player Schema
*/
var PlayerSchema = new Schema({
created: {
type: Date,
default: Date.now
},
throws: {
type: Number,
},
name: {
type: String,
default: '',
trim: true
},
position: {
type: String,
default: '',
trim: true
},
playerNum: {
type: String,
default: '',
trim: true
},
position: {
type: Number,
default: 0,
trim: true
},
teamName: {
type: String,
default: '',
trim: true
}
});
mongoose.model('Player', PlayerSchema);
Coach Schema
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Coach Schema
*/
var CoachSchema = new Schema({
created: {
type: Date,
default: Date.now
},
email: {
type: String,
unique: true,
lowercase: true,
trim: true,
default: ''
},
name: {
type: String,
default: '',
trim: true
},
players: [{
type: Schema.ObjectId,
ref: 'Player'
}
});
mongoose.model('Coach', CoachSchema);

Categories

Resources