MongoDB/Mongoose how to manage incrementation of some Number fields - node.js

I'm currently working on an API and I need the users to have at least 4 fields of number which will increase over time. Some of them might be 15000/hour, some of them 500/hour, some will be negative like -8000/hour. How should I handle such data storage using Mongoose? What kind of properties should these user model fields have? If you can provide me a simple Mongoose model for such data storage, I would be happy.
Should I have something in back-end just to increase (or decrease) these fields of users? Or does MongoDB/Mongoose have something to provide this feture? Also, how should I show these fields increasing on the web page? Should I always get the increased fields of user every few seconds? Or should I just use JavaScript on front-end?
Thanks in advance.

It would depending on what you would want to achieve with your data, at the end of the day.
To keep track of all changing logs, persisted in its respective array, then bucket pattern would best solve your problem [bucket pattern explanation][1]
[1]: https://www.mongodb.com/blog/post/building-with-patterns-the-bucket-pattern
, else just ordinary field manipulation should solve your problem.
Schema design for implementation of both, [bucket pattern] and [field manipulation] are shown as follows:
If you plan to keep track of all your changing logs, then bucket pattern is your best bet, your schema could look like this:
const mongoose = require('mongoose')
const { Schema } = mongoose
const numberLogSchema1 = new Schema({
field1: [
{type:Number}
],
field2: [
{type:Number}
],
field3: [
{type:Number}
],
field4: [
{type:Number}
]
})
module.exports = mongoose.model('numberLogs1',numberLogSchema1)
And its corresponding would look like this:
router.post('/numberLog', async (req, res) => {
try {
const saveNumberLog = await numberLog1.updateOne(
{ _id: numberCollectionIdhere },
{
$push: {
field1: req.body.fieldLogValue
}
})
res.json(saveNumberLog)
}catch (err) {
res.json({
message: err
})
}
})
Else if you just want to manipulate field values, at specific intervals from the frontend, using a javascript timer which could also be persisted to the database and fetched on a page Reload, your schema could look like this:
const mongoose = require('mongoose')
const { Schema } = mongoose
const numberLogSchema2 = new Schema({
field1: {
type: Number,
required: true,
default: 0
},
field2: {
type: Number,
required: true,
default: 0
},
field3: {
type: Number,
required: true,
default: 0
},
field4: {
type: Number,
required: true,
default: 0
}
})
module.exports = mongoose.model('numberLogs2',numberLogSchema2)
And its corresponding route paths, could look like this:
//if you intend to just increase the logvalue hourly
//without keeping track of it previous value then we use $inc
router.post('/numberLog2', async (req, res) => {
try {
const saveNumberLog2 = await numberLog2.updateOne(
{ _id: numberCollectionIdhere },
{
$inc: {
field1: req.body.fieldLogValue
}
})
res.json(saveNumberLog2)
}catch (err) {
res.json({
message: err
})
}
})

Related

Update document in MongoDB via NodeJS

So my knowledge of NodeJS and MongoDD are non-existent (just need to do a small code update for a friend) and I'm stuck.
Need to update a single document inside a collection via a unique id but can't seem to do it.
Here's the Model (I've trimmed it down and cut out all unnecessary data). I'm trying to update the field notes inside a transaction.
In short each entry in the given (an Agent) table will have a collection of multiple Transactions & Documents. I need to update a specific Transaction with the unique _id that is auto generated.
import { Schema, model } from 'mongoose';
interface Transaction {
first_name: string;
last_name: string;
type: string;
notes: string;
}
interface Agent {
org_id: number;
transactions: Array<Transaction>;
documents: Array<string>;
}
const transactionSchema = new Schema<Transaction>({
first_name: { type: String },
last_name: { type: String },
type: { type: String },
notes: String,
});
const transactionsSchema = new Schema<Agent>({
org_id: { type: Number },
transactions: [transactionSchema],
documents: [documentTypesSchema],
});
const AgentTransaction = model<Agent>(
'agent_transaction_table',
transactionsSchema
);
export default AgentTransaction;
Here's what I tried but didn't work (obviously), again I've trimmed out all unnecessary data. Just to clarify, the endpoint itself works, but the DB update does not.
import AgentTransaction from '../models/transaction'; // the above model
transaction.put('/notes', async (req, res) => {
const { org_id, transaction_id, notes } = req.body;
try {
const notesResult = await AgentTransaction.updateOne({
'transactions._id': transaction_id,
}, {
$set: {
'notes': notes
},
});
res
.status(200)
.json({ message: 'Updated', success: true, notesResult });
} catch (error) {
res.status(400).send(error);
}
});
So I figured it out. Maybe it'll help someone else as well.
const notesResult = await AgentTransaction.updateOne({
'transactions._id': { $in: [trunc2] },
}, {
$set: {
'transactions.$.notes': notes
},
});
The main issue was that the payload object needed to target the collection folder + the wildcard + the field, not just only the field.

best schema model in my case (mongoose js)

I need advice on the schema.
For now, it looks as shown below:
const accountSchema = new mongoose.Schema({
nickname:{
index:true,
type: String,
},
levels_scores: [Number],
money: Number,
skins: { circles: [Number], sticks: [Number], targets: [Number] },
current_skins: { circles: Number, sticks: Number, targets: Number },
})
my queries are always filtered by nickname so I created index on it.
I very often update money value :
saveMoney(req, res) {
const nickname,money = req.body
Accounts.findOneAndUpdate({ nickname:nickname},{money:money}),{useFindAndModify:false)
res.sendStatus(200)
}
Callback in the case will return all the document data which I don't need.(levels_scores,skins etc.) Performance waste I think
Am I thinking wrong?
Should I make schema with references,like money schema which stores only parent ID and money value?
I also have queries like:
Accounts.findOne({ nickname: req.body.nickname }, (err, account) => {
const part = req.body.skin[0]
const skin_number = req.body.skin[1]
account.skins[part].push(skin_number)
account.markModified("skins")
account.save()
res.sendStatus(200)
})
If i use .select("skins") method to not return all the document data, will I be able to save it?
How would I do that to be the most performant?

How to create a Post Request for Express/Mongoose Normalized Many-to-Many relationships

I am trying to learn how to create a Normalized many to many Post request in express/router and Mongoose.
I have three collections: User, Building and Room which is also the order of parent to child documents.
My Building document schema includes both User and Room id's as follows:
const mongoose = require('mongoose');
const BuildingSchema = new mongoose.Schema({
user: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "user"
}
],
room: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "room"
}
],
My Room document schema includes the Building ID and another child document as follows:
const mongoose = require('mongoose');
const RoomSchema = new mongoose.Schema({
building: {
type: mongoose.Schema.Types.ObjectId,
ref: "building"
},
furniture: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "furniture"
}
],
I am having trouble understanding how to create a post request that allows a user/users to create a Building instance and multiple Room instances associated with it after...
My express code so far can create a new Building instance but I am unsure how to handle including the Room id's:
router.post('/createbuilding', [auth, [
check('name', 'Building Name is required').not().isEmpty(),
]
], async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
name,
type,
website,
location,
bio,
} = req.body;
const buildingFields = {
user: req.user.id,
//room: req.room.id,
name,
type,
website: website && website !== '' ? normalize(website, { forceHttps: true }) : '',
location,
bio
};
try {
let building = await Building.findOneAndUpdate(
{ user: req.user.id },
//{ room: req.room.id },
{ $set: buildingFields },
{ new: true, upsert: true }
);
res.json(building);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
}
);
Note: The references to room ID are commented out on purpose because this is what I am unsure of how to include in the Post request.
With Mongoose's .findOneAndUpdate() the first parameter is to query for the document, similar to .findOne(). Your second parameter is what fields to update. $set is redundant since Mongoose will do that for you.
If you want to add a room to the building rather than replacing all the associations, you'll want to add the room with a $push.
const buildingFields = {
$push: {
room: {
req.room.id
}
},
name,
type,
...
}
I am also assuming that you intended that the user field in BuildingSchema is a single association rather than a many associations. If not, you'd need to use a $elemMatch to query for that document:
Mongoose Mongodb querying an array of objects

MongoDB element on nested schemas and return only that element

i have this Schema for a simple twitter app
const userSchema = new Schema ({
loginInfo: {
username: String,
email: String,
password: String
},
tweets: [{
content: String,
likes: Number,
comments: [{
owner: String,
content: String,
likes: Number
}]
}],
followers: [String],
following: [String]
})
and i want to make endpoint that return only the tweet that has the same _id that has been given as a params on the URL ..
I made that solution below and its working correctly but i believe there is a much better solution than this ..
const handleTweet = (User) => (req,res) => {
const { id } = req.params;
let theTweet = [];
User.findOne({ "tweets._id": id})
.then(user => {
user.tweets.forEach(tweet => {
if(tweet._id.toString() === id)
return theTweet.push(tweet)
})
res.json(theTweet)
})
.catch(err => res.json(err))
}
module.exports = handleTweet;
One more question : Is it better to make nested schemas like this or making a different models for each schema (in this case schema for User and another one for Tweets) ?
You should make the tweets into a different collection since you are querying based on that, and then you can use autopopulate when you need it.
Also instead of the foreach you could use Array.prototype.find
Hope this helps!
You can use the $push & findOneAndUpdate methods from mongoose. You can modify your example to be like this:
User.findOneAndUpdate(id, { $push: { tweets: req.body.tweet } }, {new: true})
.then((record) => {
res.status(200).send(record);
})
.catch(() => {
throw new Error("An error occurred");
});
Notice the {new: true} option, it makes the findOneAndUpdate method to return the record with the edit.
For your second question, it's recommended to split the modals to make your code more readable, maintainable and easy to understand.

How to location data in node express mongo

I have the following schema to store coordinates:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create Schema
const LocationSchema = new Schema({
// Matches
loc: {
type:
{
type: String
},
coordinates: [Number]
},
member_id: {
type: String
}
});
LocationSchema.index({ loc: '2dsphere' });
mongoose.model('locations', LocationSchema);
I have also created a get route to get to the page where I want to show this stored coordinates data. When I hit the route I get the following in the console.log which I am using to debug:
{ loc: { coordinates: [ 74.360106, 31.497754 ], type: 'Point' },
_id: 5b5abd4bdcbd0f4e5c70b799,
member_id: 'HyQUa4_N7',
__v: 0 }
This looks fine to me since I am getting back the coordinates and the member ID. Following is the code for my get route using which I am passing the data to my express handlebars view:
// Manage Location
router.get('/userlocation/:member_id', ensureAuthenticated, (req, res) => {
Location.findOne({ member_id: req.params.member_id })
.then(locations => {
console.log(locations);
res.render('users/userlocation', { locations: locations });
})
.catch(error => {
console.log('could not find user location');
});
});
I am confused about how do I show this data in my HTML/View. Normally I use {{variable name}} to show data which is being passed into the view. what format should I use. I tried using {{locations.coordinates[0]}} but that gives me an error.
Please help. This is the first time I am attempting something like this in Node.
You can access the longitude like this:
{{locations.loc.coordinates[0]}}
I would suggest you should define schema like this:
location: {type: [Number], index: '2d'}, // [, ]

Resources