Parse JSON Object from MongoDB GET - javascript

I have a ReactApp with a MongoDB based on local events here in my city. The current schema looks like this.
const eventSchema = mongoose.Schema({
start: String,
end: String,
name: String,
loc: { // Reference: https://mongoosejs.com/docs/geojson.html
type: {
type: String,
enum: ['Point']
},
coordinates: {
type: [Number]
}
},
web: String,
desc: String
});
I have an event button where I press, it gets the data from the DB and renders it to the HTML Page
callbackGetData() {
fetch('/api')
.then(resp => {
console.log(resp);
return resp.json();
})
.then(jdat => {
console.log(jdat);
this.setState({ data: JSON.stringify(jdat)}); // Print to debug area.
// todo: Update marker on the map?
});
}
when printed on the screen, this the results from the JSON objects that are stored in the database
[{"loc":{"coordinates":[-122.698687,45.526974],"type":"Point"},"_id":"5e5c3cde56138449ec49905f","start":".","end":".","name":"Portland Dining Month","web":".","desc":".","__v":0},
{"loc":{"coordinates":[-122.680712,45.509871],"type":"Point"},"_id":"5e5c3cde56138449ec499060","start":".","end":".","name":"Test event #PSU","web":".","desc":".","__v":0},
{"loc":{"coordinates":[-122.674043,45.481716],"type":"Point"},"_id":"5e5c3cde56138449ec499057","start":".","end":".","name":"Transgender Clients: Assessment and Planning for Gender-affirming Medical Procedures","web":".","desc":".","__v":0}]
If I try to go and print it with
console.log(jdat);
this.setState({ data: JSON.stringify(jdat.name)});
It says that it undefined. How can I go about printing out just the individual names and location coordinates and storing them in a variable I can use elsewhere in the file? Thanks

Looks like you have a array of data coming from database. so trying jdat.name will not work. You need to iterate over the array.
try the following code.Hope you are looking for the same
var jdat = [{"loc":{"coordinates":[-122.698687,45.526974],"type":"Point"},"_id":"5e5c3cde56138449ec49905f","start":".","end":".","name":"Portland Dining Month","web":".","desc":".","__v":0},
{"loc":{"coordinates":[-122.680712,45.509871],"type":"Point"},"_id":"5e5c3cde56138449ec499060","start":".","end":".","name":"Test event #PSU","web":".","desc":".","__v":0},
{"loc":{"coordinates":[-122.674043,45.481716],"type":"Point"},"_id":"5e5c3cde56138449ec499057","start":".","end":".","name":"Transgender Clients: Assessment and Planning for Gender-affirming Medical Procedures","web":".","desc":".","__v":0}]
jdat.forEach(item =>{
console.log("Name : ",item.name)
console.log("Location Coordinates : ",item.loc.coordinates)
})

Related

Mongoose - Deleting documents is unresponsive

I'm trying to use Mongoose (MongoDB JS library) to create a basic database, but I can't figure out how to delete the documents / items, I'm not sure what the technical term for them is.
Everything seems to work fine, when I use Item.findById(result[i].id), it returns a valid id of the item, but when I use Item.findByIdAndDelete(result[i].id), the function doesn't seem to start at all.
This is a snippet the code that I have: (Sorry in advance for bad indentation)
const testSchema = new schema({
item: {
type: String,
required: true
},
detail: {
type: String,
required: true
},
quantity: {
type: String,
required: true
}
})
const Item = mongoose.model("testitems", testSchema)
Item.find()
.then((result) => {
for (i in result) {
Item.findByIdAndDelete(result[i].id), function(err, result) {
if (err) {
console.log(err)
}
else {
console.log("Deleted " + result)
}
}
}
mongoose.connection.close()
})
.catch((err) => {
console.log(err)
})
I'm not sure what I'm doing wrong, and I haven't been able to find anything on the internet.
Any help is appreciated, thanks.
_id is a special field on MongoDB documents that by default is the type ObjectId. Mongoose creates this field for you automatically. So a sample document in your testitems collection might look like:
{
_id: ObjectId("..."),
item: "xxx",
detail: "yyy",
quantity: "zzz"
}
However, you retrieve this value with id. The reason you get a value back even though the field is called _id is because Mongoose creates a virtual getter for id:
Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field cast to a string, or in the case of ObjectIds, its hexString. If you don't want an id getter added to your schema, you may disable it by passing this option at schema construction time.
The key takeaway is that when you get this value with id it is a string, not an ObjectId. Because the types don't match, MongoDB will not delete anything.
To make sure the values and types match, you should use result[i]._id.

What is the best way of getting a document with a field that needs to be constantly updated?

I have this Schema for example :
const bikeSchema = new Schema<IBike>({
name: String,
color: String,
available: Boolean,
});
And also another schema which holds orders :
const orderSchema = new mongoose.Schema<IOrderSchema>({
dateStart: {
type: Date,
},
dateEnd: {
type: Date,
},
bike: { type: Schema.Types.ObjectId, ref: 'Bike' },
});
Now when I want to get one bike or get all bikes I usually have some aggregation function that I pass Date.now and it returns to me all the bikes that are available in this the time frame I pass them (but it doesnt return the bikes that dont have orders because it is aggregation on the Order schema). But I also need to call another function that gets all the bikes with no orders. Then I do bikeModel.find({_id : { $in : {...bikesWithNoOrders, ...availableBikes}}}). But does mongoose or MongoDB have a way of doing that for me everytime I get bikes.
Some way of telling him "Hey calculate if this bike is available everytime I get it and change your 'available' field"

What is the best way to keep track of changes of a document's property in MongoDB?

I would like to know how to keep track of the values of a document in MongoDB.
It's a MongoDB Database with a Node and Express backend.
Say I have a document, which is part of the Patients collection.
{
"_id": "4k2lK49938d82kL",
"firstName": "John",
"objective": "Burn fat"
}
Then I edit the "objective" property, so the document results like this:
{
"_id": "4k2lK49938d82kL",
"firstName": "John",
"objective": "Gain muscle"
}
What's the best/most efficient way to keep track of that change? In other words, I would like to know that the "objective" property had the value "Burn fat" in the past, and access it in the future.
Thanks a lot!
Maintaining/tracking history in the same document is not all recommended. As the document size will keep on increasing leading to
probably if there are too many updates, 16mb document size limit
Performance degrades
Instead, you should maintain a separate collection for history. You might have use hibernates' Javers or envers for auditing for your relational databases. if not you can check how they work. A separate table (xyz_AUD) is maintained for each table (xyz). For each row (with primary key abc) in xyz table, there exist multiple rows in xyz_AUD table, where each row is version of that row.
Moreover, Javers also support MongoDB auditing. If you are using java you can directly use it. No need to write your own logic.
Refer - https://nullbeans.com/auditing-using-spring-boot-mongodb-and-javers/
One more thing, Javers Envers Hibernate are java libraries. But I'm sure for other programming languages also, similar libraries will be present.
There is a mongoose plugin as well -
https://www.npmjs.com/package/mongoose-audit (quite oudated 4 years)
https://github.com/nassor/mongoose-history#readme (better)
Maybe you can change the type of "objective" to array and track the changes in it. the last one of the array is the latest value.
Maintain it as a sub-document like below
{
"_id": "4k2lK49938d82kL",
"firstName": "John",
"objective": {
obj1: "Gain muscle",
obj2: "Burn fat"
}
}
You can also maintain it as an array field but remember, mongodb doesn't allow you to maintain uniqueness in an array field and if you plan to index the "objective" field, you'll have to create a multi key index
I think the simplest solution would be to use and update an array:
const patientSchema = new Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
objective: { type: String, required: true }
notes: [{
date: { type: Date, default: Date.now() },
note: { type: String, required: true }
}],
});
Then when you want to update the objective...
const updatePatientObjective = async (req, res) => {
try {
// check if _id and new objective exist in req.body
const { _id, objective, date } = req.body;
if (!_id || !objective) throw "Unable to update patient's objective.";
// make sure provided _id is valid
const existingPatient = await Patient.findOne({ _id });
if (!existingPatient) throw "Unable to locate that patient.";
// pull out objective as previousObjective
const { objective: previousObjective } = existingPatient;
// update patient's objective while pushing
// the previous objective into the notes sub document
await existingPatient.updateOne({
// update current objective
$set { objective },
// push an object with a date and note (previouseObjective)
// into a notes array
$push: {
notes: {
date,
note: previousObjective
},
},
}),
);
// send back response
res
.status(201)
.json({ message: "Successfully updated your objective!" });
} catch (err) {
return res.status(400).json({ err: err.toString() });
}
};
Document will look like:
firstName: "John",
lastName: "Smith",
objective: "Lose body fat.",
notes: [
{
date: 2019-07-19T17:45:43-07:00,
note: "Gain muscle".
},
{
date: 2019-08-09T12:00:38-07:00,
note: "Work on cardio."
}
{
date: 2019-08-29T19:00:38-07:00,
note: "Become a fullstack web developer."
}
...etc
]
Alternatively, if you're worried about document size, then create a separate schema for patient history and reference the user's id (or just store the patient's _id as a string instead of referencing an ObjectId, whichever you prefer):
const patientHistorySchema = new Schema({
_id: { type: Schema.Types.ObjectId, ref: "Patient", required: true },
objective: { type: String, required: true }
});
Then create a new patient history document when the objective is updated...
PatientHistory.create({ _id, objective: previousObjective });
And if you need to access to the patient history documents...
PatientHistory.find({ _id });

How to query linked documents in mongodb

I'd like to know if there's a way to directly search within a linked collection.
I'm using Express with mongoosejs
I have the following situation.
I have 3 collections, deal, product and store and I have associated product and store with the deal collection.
const DealSchema = new Schema({
...
product: {
type: Schema.Types.ObjectId,
ref: 'product'
},
shop: {
type: Schema.Types.ObjectId,
ref: 'shop'
},
...
});
In my collection product, I have a field called upc.
I have a controller that handles deals creation, however before I create a deal, I want to check whether or not there's already a deal with the same UPC in this store if so I'll only update the confirmedBy field.
This is my controller
async create(req, res) {
const dealProps = req.body;
const product = await Products.findOne({ upc: dealProps.upc });
let deal;
if (product) {
deal = await Deals.findOne({ product: product._id });
if (deal.shop.toString() === dealProps.shop.toString()) {
const updatedDeal = await Deals.findOneAndUpdate({ product: product._id }, {
$push: {confirmedBy: dealProps.user }
});
res.send(updatedDeal);
}
} else {
deal = await (new Deals(dealProps)).save();
res.send(deal);
}
}
I've tried to search directly within the product collection like this:
const product = await Deals.findOne({'product.upc': dealProps.upc });
However it returns null.
Is there a way to search directly within a linked collection? Do I need to create an index for the upc field in the product collection?
If not, should I rethink my deals collection to add the upc and storeId to simplify the lookup?
Thank you for your help, much appreciated.

Querying multiple collections in Firebase

I've been working with Firebase for a little while, and like their suggestion to keep data denormalized. My only problem is figuring out the best way to query across multiple collections. For example, I have an Identity object which holds info on my users:
identities: {
$identity: {
name: string,
studio: $studioID
}}
That corresponds to a Studio object:
studios: {
$studio: {
name: string,
owner: $identityID,
location: $locationID
}}
This object references the owner, and also their location. The location object references Classes, which references Students.... on and on. Right now, in order to fetch a referenced object, I'm doing something like this:
Auth.loginUser(email, password, (success) => {
const identityRef = firebase.database().ref('/identity');
identityRef.child(success.uid).on("value", function(identitySnapshot) {
const identity = identitySnapshot.val();
const studioRef = firebase.database().ref('/studios');
dispatch({type: 'UPDATE_IDENTITY', identity})
studioRef.child(identity.studioID).on("value", function(studioSnapshot) {
const studio = studioSnapshot.val();
dispatch({type: 'UPDATE_STUDIO', studio});
})}); });
I would continue nesting my calls for Location, Classes, Students, etc. Is there a better way to do this?
Consider the following structures:
identities: {
$identityKey: {
name: string
}
}
identities_studios: {
$identityKey: {
$studioKey: {
name: string
}
}
}
identities_studios_locations: {
$identityKey: {
$studioKey: {
$locationKey: {
lat: string,
lng: string
}
}
}
}
The first, identities, only stores info about the identities as usual.
The second, identities_studios, only stores info about the studios, but the studios are grouped by $identityKey.
The third, identities_studios_locations, only stores info about the locations, but they are grouped firstly by $studioKey and secondly by $identityKey.
Now you can do this:
const db = firebase.database()
Auth.loginUser(email, password, success => {
db.ref(`/identities/${success.uid}`).on("value", snap => { ... }
db.ref(`/identities_studios/${success.uid}`).on("value", snap => { ... }
db.ref(`/identities_studios_locations/${success.uid}`).on("value", snap => { ... }
}
Instead of making multiple requests one after the other, we get them to run simultaneously.
If you want, after getting all this info from the database, you can transform the data structure to whatever you want: one array for identities, another for studios, another for locations, etc.; or a single array of identities with nested studios which in turn have nested locations etc.

Categories