creating array field in mongodb using mongoose - node.js

I am trying to create a collection in mongodb where a field named lists will contain an array of link and linkName. I am successfully able to create a two seperate field link and linkName, however not able to store the value inside lists.
Model code for mongodb :-
const socialSchema = new Schema({
lists: [{
link:{ formType: String},
linkName: { formType: String}
}]
})
API code :-(this code is for creating only, will later on try to use findOneAndUpdate to update the existing field
router.route('/', [auth]).post(async (req, res) => {
const {linkName, link } = req.body
try {
console.log(req.body)//Ex. { linkName: 'facebook', link: 'www.facebook.com'}
const social = new Social({
//Stuck here!!!
})
await social.save()
res.json(social)
} catch (err) {
console.error(err.message);
res.status(500).send('Server Errors')
}
}
)
Part of frontend Code(React)
const [formData, setFormData] = useState({
linkName: '',
link: ''
});
const {linkName, link} = formData
const onChange = e =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const handleSubmit = async e => {
e.preventDefault()
const socialList = {
linkName,
link
}
try {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify(socialList)
const res = await Axios.post('/api/social', body, config)
console.log(res)
} catch (err) {
console.error(err);
}
}

In your schema change from {formType: String} to {type: String}.
const data = {link: req.body.link, linkName: req.body.linkName};
Social.create({
links: [data]
});
This should work.
MY FULL WORKING CODE THAT I TESTED
const schema = new mongoose.Schema({
links: [
{
link: { type: String },
linkName: { type: String }
}
]
});
const Model = mongoose.model("test", schema);
const doc = { link: "link", linkName: "linkname" };
Model.create({
links: [doc]
});

Related

Cannot read property 'find' of undifined mongodb

I am starting to implement mongoose in a nodejs project. I have created a test record in a collection to test the CRUD operations from the back, I am trying to test the find() property of mongo but I am not sure how to do it.
This is my connection to mongoose:
const mongoose = require('mongoose');
const mongoURI: string = "mongodb://localhost:27017"
const mongoDB: string = "testdb"
export const setMongo = async() => {
try {
let mongodbURI: string = `${mongoURI}/${mongoDB}`
await mongoose.connect(mongodbURI);
console.log('conected DB')
} catch (error) {
console.log('error DB')
}
};
This is my Schema:
const mongoose = require('mongoose');
const companiesSchema = new mongoose.Schema ({
name: {
type: String,
required: true
},
phoneNumber: {
type: Number,
required: true,
unique: true
}
}, {
versionKey: false,
collection: 'companies'
});
module.exports = mongoose.model('Companies', companiesSchema);
This is my resposity.ts:
const companySchema = require("../../schemas/companies")
const db = companySchema.Companies
export class Repository {
public async getAll(): Promise<any> {
try {
console.log('getAll()')
const comp = await db.find({});
console.log(comp)
} catch (error) {
console.log(error)
}
}
}
This is the error it shows:
TypeError: Cannot read property 'find' of undefined
How should I create the connections or queries to mongo?
UPDATE
How can I get the total of the data with the find() method? Is it possible?
you just import your model in your controller and then you can use your query like:
const Companies = require("../../schemas/companies")
export class Repository {
public async getAll(): Promise<any> {
try {
console.log('getAll()')
const comp = await Companies.find({});
console.log(comp)
} catch (error) {
console.log(error)
}
}
}
and for get count of your result you can use .count() after your query to count your result :
const comp = await Companies.find({}).count();

How to use Redux to dispatch data to the backend (and consequently mongoDB)?

I recently created a simple MERN application that is supposed to use a form to send data to the backend using Redux to maintain state management. I'm new to Redux (as you will see in my code) and I believe I must have messed up the dispatching.
Below are the functions in my Form component:
const [landlordData, setLandlordData] = useState({name: '', type: '', rating: '', details: ''});
const dispatch = useDispatch();
const handleSubmit = (e) => {
e.preventDefault();
console.log(landlordData);
dispatch(createLandlord(landlordData));
}
Which console log the data from the form normally. When I submit though the new entry in the MongoDB only includes the time created and the UUID of the entry due to the Schema of the database:
import mongoose from 'mongoose';
const landlordSchema = mongoose.Schema({
name: String,
type: String,
rating: Number,
details: String,
createdAt: {
type: Date,
default: new Date()
}
});
var landlordDetails = mongoose.model('Landlords', landlordSchema);
export default landlordDetails;
To provide more context on the backend operations here is the controller script I made:
import landlordDetails from '../models/landlords.js';
export const getLandlords = async (req, res) => {
try {
const getDetails = await landlordDetails.find();
console.log(getDetails);
res.status(200).json(getDetails);
} catch (error) {
res.status(404).json({ message: error.message });
}
}
export const createLandlords = async (req, res) => {
const details = req.body;
const newLandlord = new landlordDetails(details);
try {
await newLandlord.save();
res.status(201).json(newLandlord);
console.log("New landlord added!");
} catch (error) {
res.status(409).json({ message: error.message })
}
}
Please let me know if any more information is needed or if I am completely oblivious to something obvious. Thank you.
EDIT: To provide more context, here are my api calls and my action script:
API:
import axios from 'axios';
const url = 'http://localhost:5000/landlords';
export const fetchLandlords = () => axios.get(url);
export const createLandlord = (landlordData) => axios.post(url, landlordData);
Actions JS file:
import * as api from '../api/index.js';
//Action creators
export const getLandlords = () => async (dispatch) => {
try {
const { data } = await api.fetchLandlords();
dispatch({ type: 'FETCH_ALL', payload: data });
} catch (error) {
console.log(error.message);
}
};
export const createLandlord = (landlord) => async (dispatch) => {
try {
const { data } = await api.createLandlord(landlord);
dispatch({ type: 'CREATE', payload: data });
} catch (error){
console.log(error);
}
};
When I click the submit button, a new database entry is made with the createdAt field but nothing else.

User validation failed: name: Cast to String failed for value

I am getting the above mentioned error on uploading more than one image. For one image it's working well but multiple images it's creating a problem. It shows validation error because on append two time images its also appending the name and lastname two times.
In the Front-end, I am using React and on the backend, I am using the Nodejs Express MongoDB and multer for image uploading.
Client#####
constructor(props) {
super(props);
//binding
this.onFileChange = this.onFileChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
this.state = {
imgCollection: '',
name: '',
lastName: ''
}
}
// For the req body data on change on the text input
handleChange(e) {
this.setState({
[e.target.id]: e.target.value
})
}
//for image on change
onFileChange(e) {
this.setState({ imgCollection: e.target.files })
}
//FOr form submition
onSubmit(e) {
e.preventDefault()
var formData = new FormData();
for (const key of Object.keys(this.state.imgCollection)) {
formData.append('imgCollection', this.state.imgCollection[key])
formData.append('name', this.state.name )
formData.append('lastName', this.state.lastName)
}
axios.post("http://localhost:4000/api/upload-images", formData, {
}).then(res => {
console.log(res.data)
})
/* fetch('http://localhost:4000/api/upload-images', { method: 'POST', body: formData })
.then(res => {
res.json()
console.log(res)}) */
}
Server####
router.post('/upload-images', upload.array('imgCollection', 6), (req, res, next) => {
const reqFiles = [];
const url = req.protocol + '://' + req.get('host')
for (var i = 0; i < req.files.length; i++) {
reqFiles.push(url + '/public/' + req.files[i].filename)
}
const user = new User({
_id: new mongoose.Types.ObjectId(),
imgCollection: reqFiles,
...req.body
});
user.save().then(result => {
res.status(201).json({
message: "Done upload!",
userCreated: {
_id: result._id,
imgCollection: result.imgCollection,
name: result.name,
lastName: result.lastName
}
})
}).catch(err => {
console.log(err),
res.status(500).json({
error: err
});
})
})
I noticed that you are appending formData multiple times using the same key. I don't think that would work. Generally, while adding multiple values to a field in formData - in order to append them as an array, here's what you need to do, add '[]' at the end of the key name. Here's a function you can try using to add multiple files:
this.state.imgCollection.forEach(image => formData.append('imgCollection[]', image))

Unable to access value of req.body

I am trying to do a PUT request to update my DB in Mongo using Axios so I can update the current week and season. In my route, I can access the req.body, but if I try to set the values, it says the values are undefined. I have tried many different things at this point. I ran into this issue as well: "Cast to Number failed".
Here is what the request looks like:
function currentWeek() {
const currentWeek =
"https://api.sportsdata.io/v3/nfl/scores/json/CurrentWeek?key=...";
axios.get(currentWeek).then((res) => {
const weekCheck = res.data;
const timeframeURL =
"https://api.sportsdata.io/v3/nfl/scores/json/Timeframes/current?key=...";
console.log(weekCheck);
axios.get(timeframeURL).then((res) => {
const timeframeWeek = res.data;
// console.log(timeframeWeek);
const thisWeek = timeframeWeek.filter(
(timeframeWeek) => timeframeWeek.Week === weekCheck
);
console.log(thisWeek);
const config = {
headers: {
"Content-Type": "application/json",
},
};
axios
.put("http://localhost:4000/api/currentweek/5ffce18e78d4742414cf279e", thisWeek, config)
.then((res) => console.log("working"))
.catch((err) => console.error(err));
console.log("Done!");
});
});
}
Here is my route:
router.put("/:_id", async (req, res) => {
const { Season, Week } = req.body;
const { _id } = req.params;
const weekField = {};
// SETING THE VALUES FROM REQ.BODY TO BE IN weekField
if (Season) weekField.Season = Season;
if (Week) weekField.Week = Week;
try {
let weekParam = await CurrentWeek.find({_id});
if (!weekParam) return res.stats(404).json({ msg: "ID in the Params does not exist" });
console.log(_id);
console.log(req.body) // RETURNS THE OBJECT CORRECTLY
console.log(weekField); // RETURNS AS AN EMPTY OBJECT
console.log("From Route ^^");
weekParam = await CurrentWeek.findOneAndUpdate(
_id,
{ $set: weekField },
{ new: true }
);
res.json(weekParam);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
Here is the model:
const mongoose = require('mongoose');
const CurrentWeekSchema = mongoose.Schema([{
Week: Number,
Season: Number
}]);
const CurrentWeek = module.exports = mongoose.model('currentweek', CurrentWeekSchema);
//^enables require from routes
module.exports.getCurrentWeek = function(callback, limit){
CurrentWeek.find(callback).limit(limit);
}
And here is an example of the object I am trying to take in:
[
{
SeasonType: 3,
Season: 2020,
Week: 2,
Name: 'Divisional Playoffs',
ShortName: 'Divisional',
StartDate: '2021-01-12T00:00:00',
EndDate: '2021-01-18T23:59:59',
FirstGameStart: '2021-01-16T16:35:00',
FirstGameEnd: '2021-01-16T20:35:00',
LastGameEnd: '2021-01-17T22:40:00',
HasGames: true,
HasStarted: true,
HasEnded: false,
HasFirstGameStarted: false,
HasFirstGameEnded: false,
HasLastGameEnded: false,
ApiSeason: '2020POST',
ApiWeek: '2'
}
]
I found out the issue and maybe this will be helpful for someone else in the future!
In my router file I need to change this:
const { Season, Week } = req.body;
const { _id } = req.params;
const weekField = {};
// SETING THE VALUES FROM REQ.BODY TO BE IN weekField
if (Season) weekField.Season = Season;
if (Week) weekField.Week = Week;
To this:
const data = req.body;
const { Season, Week } = data[0];
const { _id } = req.params;
const weekField = {};
// SETING THE VALUES FROM REQ.BODY TO BE IN weekField
if (Season) weekField.Season = Season;
if (Week) weekField.Week = Week;
The reason is because the data that the put request is receiving is coming in an array, so I had to set the req.body to the first object in the array.

GraphQL Resolver for Interface on same Mongoose collection

I'm creating a GraphQL server that uses Mongoose and GraphQLInterfaceType. I have a GraphQLInterfaceType of Books and sub types of SchoolBooksType and ColoringBookType. in my Mongoose Schema I specified that both SchoolBooks and ColoringBooks are to be stored in the same books collection
const coloringSchema = new Schema({
title: String,//Interface
pages: String
});
module.exports = mongoose.model("ColoringBook", coloringSchema , "books");
const schoolSchema = new Schema({
title: String, //Interface
subject: String
});
module.exports = mongoose.model("SchoolBook", schoolSchema , "books");
Here is one of my types
const SchoolBookType = new GraphQLObjectType({
name: "SchoolBook",
interfaces: [BooksInterface],
isTypeOf: obj => obj instanceof SchoolBook,
fields: () => ({
title: { type: GraphQLString },
subject: { type: GraphQLString }
})
});
Here is my query: But I don't know what to return, if I need to combine the two collections into the same array?
books: {
type: new GraphQLList(BooksInterface),
resolve() {
return SchoolBook.find({}) //<---- What to return?
}
}
Here is my query:
{
books{
title
... on ColoringBook{
pages
}
... on SchoolBook{
subject
}
}
}
Any help would be great, Thank you.
I guess you can use an async resolver, and concat both queries.
resolve: async () => {
const schoolBooks = SchoolBook.find({}).exec()
const coloringBooks = ColoringBook.find({}).exec()
const [sbooks, cbooks] = await Promise.all([schoolBooks, coloringBooks])
return [...sbooks, ...cbooks]
}

Resources