I'm new to NodeJS and Mysql databases. I make small project in React with backend. It's week calendar, where students will be able to book lessons. Right now I'm making admin panel where teacher can create a week calendar with selected available hours. All selected hours are gathered in state, and they look like this:
[
{year: '2023', user_uuid: 2, dayId: 2, hourId: 0, hour: '7:00'}
{year: '2023', user_uuid: 2, dayId: 5, hourId: 0, hour: '7:00'}
{year: '2023', user_uuid: 2, dayId: 4, hourId: 0, hour: '7:00'}
{year: '2023', user_uuid: 2, dayId: 4, hourId: 1, hour: '7:45'}
etc.]
What I'm trying to do is to send this state to my table in Mysql data base. I've created backend in NodeJS and it looks like this:
(Code edited with demonholden suggestions).
const express = require("express");
const mysql = require("mysql");
const cors = require("cors")
const app = express();
const db = mysql.createConnection({
host: "localhost",
user: "root",
password: "",
database: "meetapp"
})
app.use(express.json())
app.use(cors())
app.get("/", (req, res) =>{
res.json("hello this is the backend")
})
app.post("/terms", (req, res) => {
const q = "INSERT INTO terms (year, user_uuid, dayId,
hourId, hour) VALUES ?"
const bulkValues = req.body.map((values) => [
values.year,
values.user_uuid,
values.dayId,
values.hourId,
values.hour,
]);
console.log('bulk is', bulkValues)
db.query(q, [bulkValues], (err, data) => {
if(err) return res.json(err)
return res.json(data)
})
app.listen(8801, () => {
console.log(`Backend works! Listening on 8801`);
});
And this is my post function in React code:
const saveWeek = async e => {
e.preventDefault()
try {
await axios.post("http://localhost:8801/terms", sortedTerms)
} catch (err) {
console.log(err)
}
}
"sortedTerms" is the state with all gathered hour data, which I mentioned above. When I run onClick function saveWeek it sends all gathered data to backend. Console log in backend shows this:
bulk is [
[ '2023', 2, 0, 0, '7:00' ],
[ '2023', 2, 1, 0, '7:00' ],
[ '2023', 2, 2, 0, '7:00' ],
[ '2023', 2, 3, 0, '7:00' ],
[ '2023', 2, 3, 1, '7:45' ],
[ '2023', 2, 3, 2, '8:30' ],
[ '2023', 2, 6, 20, '22:00' ],
[ '2023', 2, 5, 20, '22:00' ]
]
EDIT: I've used demonholden suggestion and now code works fine. It saves all data to mysql database. Hope it will be helpfull for other devs.
If you are sending the entire array in the post request then you will need to specify an index position in the values assignment in your Express '/terms' post route:
const values = [
req.body[0].year,
req.body[0].user_uuid,
req.body[0].dayId,
req.body[0].hourId,
req.body[0].hour,
];
Alternatively, just send a single object in the Axios request:
axios.post('http://localhost:8801/terms', sortedTerms[0]);
If, however, you wanted to do a bulk insert with the entire array, then you will need to map req.body to an array of arrays, with each sub-array containing the values of each entry and assign the array to values:
const bulkValues = req.body.map((values) => [
values.year,
values.user_uuid,
values.dayId,
values.hourId,
values.hour,
]);
Hope this helps.
Related
const giftcards = [
{
fromuserid: 1,
amount: 10,
date: new Date(),
touserid: 11,
},
{
fromuserid: 1,
amount: 20,
date: new Date(),
touserid: 11,
},
{
fromuserid: 2,
amount: 10,
date: new Date(),
touserid: 11,
},
{
fromuserid: 2,
amount: 10,
date: new Date(),
touserid: 11,
},
{
fromuserid: 3,
amount: 10,
date: new Date(),
touserid: 11,
}
]
I achieved this, which is shown in the useEffect hook:
const giftcards = [
{
fromuserid: 1,
amounts: [{
amount: 10,
date: new Date(),
touserid: 11,
},
{
amount: 20,
date: new Date(),
touserid: 11,
}
]
},
{
fromuserid: 2,
amounts: [{
amount: 10,
date: new Date(),
touserid: 11,
},
{
amount: 10,
date: new Date(),
touserid: 11,
}
]
},
{
fromuserid: 3,
amounts: [{
amount: 10,
date: new Date(),
touserid: 11,
}]
}
]
The solution that was given works except that i would like to make it dynamic.
Meaning, in my app, I allow the user to arrange how the array will be sorted.
For example,
const [sort, setSort] = useState('fromuserid')
const [results, setResults] = useState([])
<div>
<select value={sort} onChange={(e)=> setSort(e.target.value)}>
<option value='fromuserid'>Sort by Buyer</option>
<option value='touserid'>Sort by Receiver</option>
<option value='date'>Sort by Date</option>
</select>
</div>
then in the:
useEffect(() => {
allgiftcards.forEach(({
fromuserid,
date,
from,
giftcardid,
message,
template,
touserid,
amount,
paid
}) => {
map.has(fromuserid) || map.set(fromuserid, {
fromuserid,
cards: []
})
map.get(fromuserid).cards.push({
date,
from,
giftcardid,
message,
template,
touserid,
amount,
paid
})
})
setResults([...map.values()])
}, [sort])
Here is what i mean by dynamic. If the user selected date, I would like for it to look something like:
useEffect(() => {
allgiftcards.forEach(({
fromuserid,
date,
from,
giftcardid,
message,
template,
touserid,
amount,
paid
}) => {
map.has(date) || map.set(date, {
date,
cards: []
})
map.get(date).cards.push({
date,
from,
giftcardid,
message,
template,
touserid,
amount,
paid
})
})
setResults([...map.values()])
}, [sort])
But It seems to me that having a bunch of if and else statements would be bad coding and would create a lot of extra code, so looking for a nice and clean solution
This really isn't a question of React (or where-ever useEffect comes from). This is really a general Javascript question, and it's a problem that suits itself well for solving with functional programming, or at least, with one of the staples of functional programming: reduce
In this case, you could supply the 2nd argument which is the initial value of the accumulator -- in this example an empty object works well. You can choose any key from the data to bucket the results:
// Choose a key that each object in the set has, e.g. 'fromuserid' or 'touserid'
const group_by = 'fromuserid';
let bucketed = giftcards.reduce(function (acc, x) {
let pivot = x[group_by]
let current_vals = (acc.hasOwnProperty(pivot) ? acc[pivot] : []);
current_vals.push(x);
acc[pivot] = current_vals;
return acc
}, {});
console.log(bucketed);
If you really needed to land on the second data structure you shared, you could jostle your initialValue and the exact placement of values into the accumulator, but hopefully this demonstrate the concept of how to dynamically choose how to group the data.
This codepen shows how to dynamically bucket an array of objects based on a specific property. Below is a sample of the callback function you can pass to the reducer -- included in the example.
function groupBy(accum, current, groupKey){
const val = current[groupKey]
const {[groupKey]:_, ...content} = current
if (val in accum){
accum[val].push(content)
}
else accum[val] = [content]
return accum
}
I am new to mongodb and couldn't for some reason find anything in the documentation (Maybe I looked in the wrong places).
I got two arrays, say:
ids = [1, 2, 3, 4]
values = [12, 33, 44, 11]
Currently I am looping through the list ids and am updating the DB for each entry which seems incredibly inefficient:
For that I am using an object for each iteration (Simplified):
update['values']['duration'] = values[i];
This is how I insert into the DB
await CollectionName.updateOne({ids: ids[i]}, {$set: update});
Any pointers? Thanks! :)
Edit:
Example 2:
ids = [4, 7, 9]
values = [
{"array":
"preference" : "test",
"1Duration" : 55,
"2Duration" : 66
},
{"array":
"preference" : "test",
"1Duration" : 22,
"2Duration" : 33
},
{"array":
"preference" : "test",
"1Duration" : 78,
"2Duration" : 11
}
]
Your code should look something like this:
User.findOneAndUpdate(
{ }, //This is where you could insert into a spesific record
{ $push: { ids , values },
(err) => {
if (err) throw console.log('found errors');
console.log('record created')
});
You can use $in operator in this case.
await CollectionName.updateMany({ids: {$in: ids}}, {$set: update});
I am using react-native-gifted-chat(https://github.com/FaridSafi/react-native-gifted-chat) to create a chat interface on my app, I want to load messages from my database.
I am using realm, and I am able to load the data but my code below loads only the first row of the data. I want to be able to load all the data from the database.
let chatData = realmDatabase.objects(DatabaseTableNames.chatTable);
let data=[];
for (let message of chatData ){
data = [{
_id: message.chatUniqueID,
text: message.msgBody,
createdAt: (new Date()).getTime(),
user: {
_id: message.chatUniqueID,
name: message.senderName
}
} ]
}
console.log(data)
I want to be able to load all the data from the database not only the first row, like the sample below.
[
{
_id: Math.round(Math.random() * 1000000),
text:
"It uses the same design as React, letting you compose a rich mobile UI from declarative components https://facebook.github.io/react-native/",
createdAt: new Date(Date.UTC(2016, 7, 31, 17, 20, 0)),
user: {
_id: 1,
name: "Developer"
},
},
{
_id: Math.round(Math.random() * 1000000),
text: "React Native lets you build mobile apps using only JavaScript",
createdAt: new Date(Date.UTC(2016, 7, 30, 17, 20, 0)),
sent: true,
received: true,
user: {
_id: 2,
name: "Developer"
},
}
];
Doing data = [{...}] in the for loop, will assign the last value of message to data. To get all the values, you need to push the items in the data array. You can do it like this:
let chatData = realmDatabase.objects(DatabaseTableNames.chatTable);
let data=[];
for (let message of chatData ){
data.push({
_id: message.chatUniqueID,
text: message.msgBody,
createdAt: (new Date()).getTime(),
user: {
_id: message.chatUniqueID,
name: message.senderName
}
});
}
console.log(data)
i am trying to build a chat application.. i am not sure how to structure my mongodb schema..
currently i am using this schema..
collection name: conversations
{
conversation: {
id: 1,
name: test
},
messages: [
{
_id: mongoid("123"),
userID: 1,
message: "test message"
date: date..,
status: 1,
pins: [2, 3] //message pin by user ids
mentions: [4, 5],
replyMsgID: mongoid(456)
},
...
],
users: [
{
userID: 1,
blocked: 1,
lastActiveTime: dateTime,
},
...
]
}
...
i am thinking another schema like this...
{
conversation: {
id: 1,
name: test
},
messages: [
{
_id: mongoid("999")
msgID: mongoID("123")
},
{
_id: mongoid("888")
msgID: mongoID("456")
},
...
],
users: [
{
userID: 1,
blocked: 1,
lastActiveTime: dateTime,
},
...
]
}
messages collection..
messages: [
{
_id: mongoid("123"),
userID: 1,
message: "test message"
date: date..,
status: 1,
pins: [2, 3] //message pin by user ids
mentions: [4, 5],
replyMsgID: mongoid(456),
conversationID: 1
},
...
]
i am new to mongodb and i am confused which schema should be use for a scalable for chat application..
i thinks the problems i will face with schema 1 are..
at some point the document size might get pass 16mb limit of mongo...
when fetch or update anything like if i need only 1 message or just a single user the mongo will return the whole document with thousands of messages
the problem with 2nd schema ...
i will need to join two collections with aggregate framework and might need to use $unwind, $group a lot... which i think will be very performance heavy...
to create a new message i will have to insert the message in the messages collection first then insert the the newly added message to the conversations collections property messages field, same goes for delete.. so lots of query.. :|
it would be really helpful if someone could help me out to select a schema.. or create a better schema..
thanks in advance :)
I have a user_batch collection. It contains following documents:
[{
_id: ObjectId("594baf96256597ec035df23c"),
name: "Batch 1",
batchSize: 30,
users:[]
},
{
_id: ObjectId("594baf96256597ec035df234"),
name: "Batch 2",
batchSize: 50,
users:[]
}]
In find query I want to project only name and batchSize. But when I execute find query from nodejs, I'm getting entire document in query result. Query:
db.collection('user_batch').find({}, {name: 1, batchSize: 1}).toArray((err, result) => {
if(err)
console.log(err)
else
console.log(result)
})
If I just pass {name: 1} then it will project _id and name. But if I pass batchSize then it will return entire document.
Note: I'm not facing this issue while executing this query in Mongo Shell
You are correct that the driver incorrectly interprets this as the batchSize option and ignores the projection statement.
The correct way to do this though in modern driver releases is to actually use the .project() "cursor method" instead. This is more consistent with other language driver implementations.
db.collection('collection').find()
.project({ name: 1, batchSize: 1})
.toArray();
As a full demonstration:
const mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient;
(async function() {
let db;
try {
db = await MongoClient.connect('mongodb://localhost/test');
// New form uses .project() as a cursor method
let result = await db.collection('collection').find()
.project({ name: 1, batchSize: 1})
.toArray();
console.log(JSON.stringify(result,undefined,2));
// Legacy form confuses this as being a legacy "cursor option"
let other = await db.collection('collection')
.find({},{ name: 1, batchSize: 1 })
.toArray();
console.log(JSON.stringify(other,undefined,2));
} catch(e) {
console.error(e)
} finally {
db.close()
}
})()
Produces the output:
[
{
"_id": "594baf96256597ec035df23c",
"name": "Batch 1",
"batchSize": 30
},
{
"_id": "594baf96256597ec035df234",
"name": "Batch 2",
"batchSize": 50
}
]
[
{
"_id": "594baf96256597ec035df23c",
"name": "Batch 1",
"batchSize": 30,
"users": []
},
{
"_id": "594baf96256597ec035df234",
"name": "Batch 2",
"batchSize": 50,
"users": []
}
]
Where the first output form is the corrected one, using .project()
The syntax of Find has changed. Below is what I needed to know to solve this problem. This is excerpted from https://github.com/mongodb/node-mongodb-native/blob/master/CHANGES_3.0.0.md#find
Find
find and findOne no longer support the fields parameter. You can achieve the same results as
the fields parameter by using Cursor.prototype.project or by passing the projection property
in on the options object . Additionally, find does not support individual options like skip and
limit as positional parameters. You must either pass in these parameters in the options object,
or add them via Cursor methods like Cursor.prototype.skip.
2.x syntax:
const cursor = coll.find({ a: 42 }, { someField: 1 });
3.x syntax:
const cursor = coll.find({ a: 42 }).project({ someField: 1 });
/* OR */
const cursor = coll.find({ a: 42 }, { projection: { someField: 1 } });