I'm new to JavaScript and Node etc but enjoying the experience of developing - last serious development i did was in the 90s - I used to be a 370 assembler programmer back 30 years ago!
I've been stuck on this all day.
My issue is that I am trying to add a new element to the entries in an array of objects. I have simplified my code here in the hope that I am doing something blindingly stupid and obviously wrong though I have tried this in javascript in the browser and I know I am barking up the right tree.
What I am trying to do is add a new key/value pair to the collectibles returned. I have simplified the code here but in essence the value is coming for a different collection. the issue is that the lines that set the new keys are having no effect on my objects in the array collectibles. I have tried several methods all of which I believe should work.
I can read the key/values in the returned array of objects and I can also set existing keys to new values, but setting a new key is not working.
Here is my code:
var sendJsonResponse = function (res, status, content) {
res.status(status);
res.json(content);
};
var mongoose = require("mongoose");
var Coll = mongoose.model("collectible");
var Img = mongoose.model("image");
module.exports.collectiblesList = function (req, res) {
var firstImage_ids = [];
var imageDir = "https://s3.amazonaws.com/xxxxxxxxxxx/images/";
var defaultImage = imageDir + "default.jpg";
// get the collectibles
Coll.find(req.body).exec(function (err, collectibles) {
if (!collectibles) {
// not found
sendJsonResponse(res, 404, {"message": "no collectibles found"});
return;
}
if (err) {
console.log(collectible);
sendJsonResponse(res, 400, err);
return;
}
//found the collectibles
// now find the list of images we need to pull (each collectible can have multiple images
collectibles.forEach(function (collectible, index) {
if (collectible.image_ids) {
firstImage_ids.push(collectible.image_ids[0]);
collectibles[index].imageUrl = defaultImage; // this is the line that has no effect.
collectible.imageUrl = defaultImage; // tried this too.
collectible["imageUrl"] = defaultImage; // and this. :-(
console.log(collectible);
}
});
sendJsonResponse(res, 200, collectibles);
});
};
Using Postman to test this API. This is what is being returned:
[
{
"_id": "5a43c61134aaea2025158cab",
"name": "A-0_5",
"title": "Occoneechee Lodge 104: A-0_5",
"issueDate": null,
"quantity": null,
"event_id": null,
"type": "A - Arrowhead patches",
"tag_ids": [
"5a401a17b720186ab61c649e"
],
"description": "Elangomat, no name",
"organisation_id": "5a413340b720186ab61c661e",
"supercedes_id": null,
"sortName": "A-000.5",
"image_ids": [
"5a41685ab720186ab61c663d"
],
"deleted": false,
"schemaVersion": "1.0"
},
{
"_id": "5a43c63334aaea2025158cac",
"name": "A-1",
"title": "Occoneechee Lodge 104: A-1",
"issueDate": null,
"quantity": null,
"event_id": null,
"type": "A - Arrowhead patches",
"tag_ids": [
"5a401a17b720186ab61c649e"
],
"description": "Service",
"organisation_id": "5a413340b720186ab61c661e",
"supercedes_id": null,
"sortName": "A-001",
"image_ids": [
"5a41685ab720186ab61c663e"
],
"deleted": false,
"schemaVersion": "1.0"
}]
i.e. it contains no imageUrl keys.
thanks for the assist.
WWW
GT
I might have misunderstood. But if your issue is that collectibles[i].imageUrl is later on returning undefined/null, then i believe it's simply because you need to add
collectibles.save();
after setting the collectibles[i].imageUrl value. you can add that line after the foreach loop, before the json response.
collectibles.forEach(function (collectible, index) {
if (collectible.image_ids) {
firstImage_ids.push(collectible.image_ids[0]);
collectible.imageUrl = defaultImage;
}
});
collectibles.save(); //here, this saves the changes done to the mongoose object.
sendJsonResponse(res, 200, collectibles);
I solved it in the end after a lot of effort. This article explains (but i don't fully yet follow the explanation)
I found that the object has more metadata in it and the actual object is in _doc, so the first (and i suspect frowned upon solution) is to update the code to:
collectible._doc.imageUrl = defaultImage;
The better way was to simply add imageUrl to the mongoose model and leave _doc out of it. The issue there is that this is not a field i intend to populate in the mongoDB.
Related
I have been working on the backend of my app. At this point, it can access all data in a data base, and output it. I'm trying to implement some queries, so that the user can filter out the content that is returned. My DAL/DAO, looks like this
let mflix //Creates a variable used to store a ref to our DB
class MflixDAO {
static async injectDB(conn){
if(mflix){
return
}
try{
mflix = await conn.db(process.env.JD_NS).collection("movies")
}catch(e){
console.error('Unable to establish a collection handle in mflixDAO: ' + e)
}
}
// Creates a query to fetch data from the collection/table in the DB
static async getMovies({
mflix.controller
filters = null,
page = 0,
moviesPerPage = 20,
} = {}) {
let query
if (filters){
// Code
if("year" in filters){
query = {"year": {$eq: filters["year"]}}
}
// Code
}
// Cursor represents the returned data
let cursor
try{
cursor = await mflix.find(query)
}catch(e){
console.error('Unable to issue find command ' + e)
return {moviesList: [], totalNumMovies: 0}
}
const displayCursor = cursor.limit(moviesPerPage).skip(moviesPerPage * page)
try{
const moviesList = await displayCursor.toArray() // Puts data in an array
const totalNumMovies = await mflix.countDocuments(query) // Gets total number of documents
return { moviesList, totalNumMovies}
} catch(e){
console.error('Unable to convert cursor to array or problem counting documents ' + e)
return{moviesList: [], totalNumMovies: 0}
}
}
}
export default MflixDAO
Just so you know, I am using a sample database from MongoDB Atlas. I am using Postman to test HTTP requests. All the data follows JSON format
Anyway, when I execute a basic GET request. The program runs without any problems. All the data outputs as expected. However, if I execute something along the lines of
GET http://localhost:5000/api/v1/mflix?year=1903
Then moviesList returns an empty array [], but no error message.
After debugging, I suspect the problem lies either at cursor = await mflix.find(query) or displayCursor = cursor.limit(moviesPerPage).skip(moviesPerPage * page), but the callstacks for those methods is so complex for me, I don't know what to even look for.
Any suggestions?
Edit: Here is an example of the document I am trying to access:
{
"_id": "573a1390f29313caabcd42e8",
"plot": "A group of bandits stage a brazen train hold-up, only to find a determined posse hot on their heels.",
"genres": [
"Short",
"Western"
],
"runtime": 11,
"cast": [
"A.C. Abadie",
"Gilbert M. 'Broncho Billy' Anderson",
"George Barnes",
"Justus D. Barnes"
],
"poster": "https://m.media-amazon.com/images/M/MV5BMTU3NjE5NzYtYTYyNS00MDVmLWIwYjgtMmYwYWIxZDYyNzU2XkEyXkFqcGdeQXVyNzQzNzQxNzI#._V1_SY1000_SX677_AL_.jpg",
"title": "The Great Train Robbery",
"fullplot": "Among the earliest existing films in American cinema - notable as the first film that presented a narrative story to tell - it depicts a group of cowboy outlaws who hold up a train and rob the passengers. They are then pursued by a Sheriff's posse. Several scenes have color included - all hand tinted.",
"languages": [
"English"
],
"released": "1903-12-01T00:00:00.000Z",
"directors": [
"Edwin S. Porter"
],
"rated": "TV-G",
"awards": {
"wins": 1,
"nominations": 0,
"text": "1 win."
},
"lastupdated": "2015-08-13 00:27:59.177000000",
"year": 1903,
"imdb": {
"rating": 7.4,
"votes": 9847,
"id": 439
},
"countries": [
"USA"
],
"type": "movie",
"tomatoes": {
"viewer": {
"rating": 3.7,
"numReviews": 2559,
"meter": 75
},
"fresh": 6,
"critic": {
"rating": 7.6,
"numReviews": 6,
"meter": 100
},
"rotten": 0,
"lastUpdated": "2015-08-08T19:16:10.000Z"
},
"num_mflix_comments": 0
}
EDIT: It seems to be a datatype problem. When I request a data with a string/varchar type, the program returns values that contain that value. Example:
Input:
GET localhost:5000/api/v1/mflix?rated=TV-G
Output:
{
"_id": "XXXXXXXXXX"
// Data
"rated" = "TV-G"
// Data
}
EDIT: The problem has nothing to do with anything I've posted up to this point it seems. The problem is in this piece of code:
let filters = {}
if(req.query.year){
filters.year = req.query.year // This line needs to be changed
}
const {moviesList, totalNumMovies} = await MflixDAO.getMovies({
filters,
page,
moviesPerPage,
})
I will explain in the answer below
Ok so the problem, as it turns out, is that when I make an HTTP request, the requested value is passed as a string. So in
GET http://localhost:5000/api/v1/mflix?year=1903
the value of year is registered by the program as a string. In other words, the DAO ends up looking for "1903" instead of 1903. Naturally, year = "1903" does not exist. To fix this, the line filters.year = req.query.year must be changed to filters.year = parseInt(req.query.year).
I am trying to get extended retweets, but I cant seem to get that extra field called retweeted_status as a parameter in the result. I don't get any errors when including a parameter called "retweeted_status" and I don't know if it even exists as a parameter in Node.js since Twitter API docs doesn't include any type of examples or clear information for retweets and I tried to implement methods written in other programming lenguages that I searched in Google but didn't get any good results at the end. (Note: tweet_mode only works for Tweets not RETweets!)
Things I tried:
retweeted_status: 'extended' //doesn't work but node.js doesn't give an error
retweeted_status: true //doesn't work but node.js doesn't give an error
retweeted_status: 1 //doesn't work but node.js doesn't give an error
My code is the following (It never enters the if-condition and if it does it gives the error I mention in the code):
var params = {q: '#Avengers', count: 100, include_rts: 0, tweet_mode: 'extended', retweeted_status: 'extended'};
client.get('search/tweets', params, function(error, tweets, response) {
if (!error) {
console.log(tweets);
for(var i=0; i < params.count; i++){
if (tweets.retweeted_status){ //It never enters this condition
//even if it does I get this error
//TypeError: Cannot read property 'statuses' of undefined tweets.statuses[i]
tweets = tweets.retweeted_status;
var tweet = (JSON.stringify(tweets.statuses[i].full_text)+"\n");
var string = tweet.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g, '');
console.log('hi');
}
else{
var tweet = (JSON.stringify(tweets.statuses[i].full_text)+"\n");
var string = tweet.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g, '');
console.log('hi2');
}
fs.appendFile("tweet.txt", string, function (err) {
if (err) throw err;
console.log('Saved!');
});
}
}
});
I looked for a few hours into this website https://dev.to/kehers/formatting-tweets-a-look-at-extended-tweets-retweets-and-quotes-n5j and tried to make some code (that I already provided above) which didn't work since the IF-statement got ignored. When getting the tweet it should give an extra parameter "retweeted_status" like below:
{
"created_at": "Sun Mar 11 12:00:27 +0000 2018",
"id": 972804442667003900,
"full_text": "RT #jasongorman: As a physics grad, I understand how snooker works at a level I imagine a lot of pro snooker players don't. But I suck at s…",
"truncated": false,
"display_text_range": [...],
"entities": {...},
"retweeted_status": { // <======================= I am not getting this extra parameter!
"created_at": "Sun Mar 11 08:10:46 +0000 2018",
"id": 972746641957642200,
"full_text": "As a physics grad, I understand how snooker works at a level I imagine a lot of pro snooker players don't. But I suck at snooker. Understanding != ability.",
"truncated": false,
"display_text_range": [0, 155],
"entities": {
"hashtags": [],
"symbols": [],
"user_mentions": [],
"urls": []
},
}
}
I'm new in Firebase. I would like to create an app (using Angular and AngularFire library), which shows current price of some wares. I have list all available wares in Firebase Realtime Database in the following format:
"warehouse": {
"wares": {
"id1": {
"id": "id1",
"name": "name1",
"price": "0.99"
},
"id2": {
"id": "id2",
"name": "name2",
"price": "15.00"
},
... //much more stuff
}
}
I'm using ngrx with my app, so I think that I can load all wares to store as an object not list because normalizing state tree. I wanted load wares to store in this way:
this.db.object('warehouse/wares').valueChanges();
The problem is wares' price will be refresh every 5 minutes. The number og wares is huge (about 3000 items) so one response will be weight about 700kB. I know that I will exceed limit downloaded data in a short time, in this way.
I want limit the loading data to interesing for user, so every user will can choose wares. I will store this choices in following way:
"users": {
"user1": {
"id": "user1",
"wares": {
"id1": {
"order": 1
},
"id27": {
"order": 2
},
"id533": {
"order": 3
}
},
"waresIds": ["id1", "id27", "id533"]
}
}
And my question is:
Is there a way to getting wares based on waresIds' current user? I mean, does it exist way to get only wares, whose ids are in argument array? F.e.
"wares": {
"id1": {
"id": "id1",
"name": "name1",
"price": "0.99"
},
"id27": {
"id": "id27",
"name": "name27",
"price": "0.19"
},
"id533": {
"id": "id533",
"name": "name533",
"price": "1.19"
}
}
for query like:
this.db.object('warehouse/wares').contains(["id1", "id27", "id533"]).valueChanges();
I saw query limits in Angular Fire like equalTo and etc. but every is for list. I'm totally confused. Is there anyone who can help me? Maybe I'm making mistakes in the design of the app structure. If so, I am asking for clarification.
Because you are saving the ids inside user try this way.
wares: Observable<any[]>;
//inside ngOnInit or function
this.wares = this.db.list('users/currentUserId/wares').snapshotChanges().map(changes => {
return changes.map(c => {
const id = c.payload.key; //gets ids under users/wares/ids..
let wares=[];
//now get the wares
this.db.list('warehouse/wares', ref => ref.orderByChild('id').equalTo(id)).valueChanges().subscribe(res=>{
res.forEach(data=>{
wares.push(data);
})
});
return wares;
});
});
There are two things you can do. I don't believe Firebase allows you to query for multiple equals values at once. You can however loop over the array of "ids" and query for each one directly.
I am assuming you already queried for "waresIds" and you've stored those ID's in an array named idArray:
for id in idArray {
database.ref('warehouse/wares').orderByChild('id').equalTo(id).once('value').then((snapshot) => {
console.log(snapshot.val());
})
}
In order to use the above query efficiently you'll have to index your data on id.
Your second option would be to use .childChanged to get only the updated data after your initial fetch. This should cut down drastically on the amount of data you need to download.
Yes , you can get exactly data that you want in firebase,
See official Firebase documents about filtering
You need to get each waresID
var waresID = // logic to get waresID
var userId = // logic to get userId
var ref = firebase.database().ref("wares/" + userId).child(waresID);
ref.once("value")
.then(function(snapshot) {
console.log(snapshot.val());
});
this will return only data related to that waresID or userId
Note: this is javascript code, i hope this will work for you.
I know this will be a very stupid question, but I've been pulling my hair out trying to figure this out. I'm getting the following response back from an API I'm using:
{
"item_id": "51c3d78797c3e6d8d3b546cf",
"item_name": "Cola, Cherry",
"brand_id": "51db3801176fe9790a89ae0b",
"brand_name": "Coke",
"item_description": "Cherry",
"updated_at": "2013-07-09T00:00:46.000Z",
"nf_ingredient_statement": "Carbonated Water, High Fructose Corn Syrup and/or Sucrose, Caramel Color, Phosphoric Acid, Natural Flavors, Caffeine.",
"nf_calories": 100,
"nf_calories_from_fat": 0,
"nf_total_fat": 0,
"nf_saturated_fat": null,
"nf_cholesterol": null,
"nf_sodium": 25,
"nf_total_carbohydrate": 28,
"nf_dietary_fiber": null,
"nf_sugars": 28,
"nf_protein": 0,
"nf_vitamin_a_dv": 0,
"nf_vitamin_c_dv": 0,
"nf_calcium_dv": 0,
"nf_iron_dv": 0,
"nf_servings_per_container": 6,
"nf_serving_size_qty": 8,
"nf_serving_size_unit": "fl oz",
}
And this is the code that I'm trying to run:
var rp = require('request-promise');
module.exports = {
getIngredients: function(req, callback) {
rp({
method: 'GET',
uri: `https://api.nutritionix.com/v1_1/item?upc=${req.body.upc}&appId=${process.env.NUTRITIONIX_APP_ID}&appKey=${process.env.NUTRITIONIX_APPP_KEY}`
}).then((data) => {
console.log(`Talked to NutritionixAPI, result was: ${data}`);
var ingredients = data.nf_ingredient_statement.split(',');
console.log(`Ingredients split from the data are: ${ingredients}`);
return callback(ingredients);
}).catch((err) => {
console.log(`Error occured in NutritionixAPI, ${err}`)
return callback(Object.assign({}, err, { error: true }));
});
}
}
What I'm trying to figure out is why data gets printed to the console properly, but as soon as I try to access any value inside, I get the error of it being undefined. I've tried other values in the JSON as well, so I would very much appreciate the help!
EDIT: I want to clarify what the question is about, it's not about the callback and async calls because those work perfectly. My issue is specifically with var ingredients = data.nf_ingredient_statement.split(','); where nf_ingredient_statement is undefined even though obviously it isn't.
Apparently what I was getting back was a JSON string. So I just needed to do data = JSON.parse(data) to parse it into actual JSON.
The problem is that data is a JSON string so you can't access it before parsing it, that's why data.nf_ingredient_statement is undefined.
You need to parse data first, your code should be like this:
var json = JSON.parse(data);
var ingredients = json.nf_ingredient_statement.split(',');
I am building a simple car database app. When a user adds a new car it creates the below JSON data with blank fields for the gallery:
{
"name": "Cars",
"gallery": [{
"img": "http://",
"title": "BMW"
}, {
"img": "http://",
"title": "Lexus"
}, {
"img": "http://",
"title": "Ferrari"
}],
"pageId": "23",
"storeURL": "/app/cars/gallery"
}
Now what I want to do in the app is if a user adds a new gallery image it should add new child after the last gallery picture. So after Ferrari it should insert a new object. I cannot seem to do this as every tutorial leads to a dead end and also the official Google docs specify that set() will replace all data so if you use update() it will add and in this case it does not add.
This code deletes all data:
function add(){
var data = [
{
title: 'Mercedes',
img: 'http://'
}
]
var postData = {
pageGallery: data
};
var updates = {};
updates['/app/cars/'] = postData;
firebase.database().ref().update(updates);
console.log('New car added');
}
And if I change and add the child:
firebase.database().ref().child('gallery').update(updates);
It does not do anything to do the data. Please can you help?
See this blog post on the many reasons not use arrays in our Firebase Database. Read it now, I'll wait here...
Welcome back. Now that you know why your current approach will lead to a painful time, let's have a look at how you should model this data. The blog post already told you about push IDs, which are Firebase's massively scalable, offline-ready approach to array-like collections.
As our documentation on reading and writing lists of data explains, you can add items to a list by calling push().
So if this creates a car:
function addStore(){
var rootRef = firebase.database().ref();
var storesRef = rootRef.child('app/cars');
var newStoreRef = storesRef.push();
newStoreRef.set({
name: "Cars",
"pageId": "23",
"storeURL": "/app/cars/gallery"
});
}
Then to add an image to the gallery of that store:
var newCarRef = newStoreRef.child('gallery').push();
newCarRef.set({
title: 'Mercedes',
img: 'http://'
})
This will add a new car/image to the end of the gallery (creating the gallery if it doesn't exist yet).
If somebody wants it With Error Handling :
var rootRef = firebase.database().ref();
var storesRef = rootRef.child('Lorelle/Visitors');
var newStoreRef = storesRef.push();
newStoreRef.set($scope.CarModal,
function(error) {
//NOTE: this completion has a bug, I need to fix.
if (error) {
console.log("Data could not be saved." + error);
Materialize.toast('Error: Failed to Submit' + error, 2000);
} else {
console.log("Data saved successfully.");
Materialize.toast('Data Submitted, Thank You.', 2000);
}
});
}