Push 2 kind of Id in array - json

Good day,
I've been studying json and mongoDB this past few months and I'm trying to push my user's ID and my product ID into my order's array. but I'm getting the error "Cannot read property 'push' of undefined"
this is the part of the code that is having a problem.
let isUserIdPush = await User.findById(data.userId).then(user => {
user.usersOrder.push({productId : data.productId});
return user.save().then((user, error) => {
if(error){
return false;
}
else{
return true;
}
})
})
I check my models and there is nothing wrong with it and I guess that my productId is the fault
edit:
I remove the models.

Related

Angular/Http "put" method only returning undefined?

I've been trying to make a very basic app pulling user information from a .json file and "logging them in", and have a json storing whether a user is logged into the app and their user ID. I'm stuck on a method which would take the given email and password, match them to an entry in the users json, then update the login json with the new information (login true, and user ID.) This is what I have so far in the method:
setUserLogIn(email, password):any{
if (this.users){
this.users.forEach(foundUser => {
if (foundUser.email === email && foundUser.password === password){
this.currentUser=foundUser;
let login:Login = {"id": 1, "loginStatus":true, "userId":foundUser.id}
return this.httpService.put<Observable<any>>('http://localhost:7800/loginCheck/1', login)
.pipe(map((log:Observable<Login>) =>{
console.log(log) //this isn't reached, never prints in console
if (log !== undefined){
return true;
}
return false;
}))
}
if (this.currentUser != null){
FetchUserService.isLoggedIn = true;
} else{
FetchUserService.isLoggedIn = false;
}
})
}
}
From my previous tests I know everything else in the method works correctly, just the put only returns undefined. I am subscribing to the method in the controller:
this.fetchUserService.setUserLogIn(this.userEmail, this.userPassword).subscribe(data => {
console.log(data);
})
This method of subscription returns an error. I also tried subscribing in the service itself, like:
return this.httpService.put<Observable<any>>('http://localhost:7800/loginCheck/1', login)
.pipe(map((log:Observable<Login>) =>{
console.log(log)
if (log !== undefined){
log.subscribe(data => {
return data
})
Taking this into the component and logging the result also just returns undefined.
Any suggestions? I have no idea what I'm doing wrong, after searching put methods for the past few hours I can't see any differences in what I have there. Any help is greatly appreciated!
There are multiple issues here.
Parallel subscriptions. Avoid them if possible. Here you could use forkJoin to combine all observables and trigger them in parallel.
Why would an HTTP request emit an Observable as it's response? Most probably it wouldn't.
Currently you aren't returning anything from the function.
Try the following
setUserLogIn (email, password): Observable<any> { // <-- return `Observable` here
if (!this.users) return NEVER;
return forkJoin(
this.users.map(foundUser => {
if (foundUser.email === email && foundUser.password === password) {
this.currentUser = foundUser;
FetchUserService.isLoggedIn = true;
let login: Login = {
"id": 1,
"loginStatus": true,
"userId": foundUser.id
};
return this.httpService.put('http://localhost:7800/loginCheck/1', login).pipe(
map((log: Login) => { // why would an HTTP request emit an observable?
console.log(log);
return (!!log);
})
);
}
FetchUserService.isLoggedIn = false;
return EMPTY; // `forkJoin` emits only when all observables complete
})
);
}

How to get Spotify playlist tracks and parse the JSON?

I am trying to figure out how to parse the JSON data that I am getting from the Spotify API. I am using this node module https://www.npmjs.com/package/spotify-web-api-js to get Spotify playlist tracks.
I am using this to GET my json (see what I did there)
export class HomePage {
spotifyApi = new SpotifyWebApi;
constructor() {}
}
var spotifyApi = new SpotifyWebApi();
spotifyApi.setAccessToken('Spotify OAuth Token');
spotifyApi.getPlaylistTracks('37i9dQZEVXbMDoHDwVN2tF')
.then(function(data) {
console.log('Playlist Tracks', data);
}, function(err) {
console.error(err);
var prev = null;
function onUserInput(queryTerm) {
// abort previous request, if any
if (prev !== null) {
prev.abort();
}
// store the current promise in case we need to abort it
prev = spotifyApi.searchTracks(queryTerm, {limit: 5});
prev.then(function(data) {
// clean the promise so it doesn't call abort
prev = null;
// ...render list of search results...
}, function(err) {
console.error(err);
});
}
This returns a JSON file but for some reason (probably my mistake) when I use JSON.parse(data);
console.log(data.name) it doesn't work (I know I am doing something wrong here but I don't know how to fix it). Thanks in advance :{)
If you want to get the tracks from the url you have to do this data.tracks.track[0] replace 0 with the needed tracks.

Can't handle json files returned by Observable.forkJoin()

I'm working with Angular2 and a nodejs rest api. I have to do one or more http request for a same task so I'm using Observable.forkJoin() to wait for all of them to finish.
I map the result with the json parsing method and then subscribe to this result but I can't get any json properties from the result the way I used to do.
My service method returns the Observable.forkJoin() itself:
public rename(file:MyFile, newName:string){
let requests = new Array();
for(let i=0; i<file.sources.length; i++){
let url:string = this.serverUrl;
if(src.name === "src1"){
url += "rename/src1";
} else if (src.name === "src2" ){
url += "rename/src2";
}
requests[i] = this.http.get(url)
.map((res:Response) => res.json())
.catch(this.handleError);
}
return Observable.forkJoin(requests);
}
Then I subscribe to it in another method elsewhere:
this.api.rename(this.selectedFile, newFileName).subscribe(
rep => {
// The editor tells me "Property 'name' doesn't exist on type '{}'."
console.log(rep[0].name);
},
err => { console.error(err); }
);
The server correctly respond with the data I asked. The rep[0] is correctly set, it looks like this:
Object {name: "res.png", id: "HyrBvB6H-", size: 0, type: "", isShared: falseā€¦}
I suppose it's a typing problem. Usually, with a simple http.get request, it returns an 'any' object. Here it returns an '[]{}' object. res[0] is an '{}' object and I can't get the json properties on it.
Am I using the Observer.forkJoin() correctly? Am I missing something?
Thanks in advance for help :)
If is the editor complaining and it is not an error when the code executes, it likely is a typing problem. You can set the return type of rename() to:
public rename(file:MyFile, newName:string): Observable<any[]> { }
This should allow you access properties of the inner results such as name.
Or you can type the rep array in subscribe() as any[]:
this.api.rename(this.selectedFile, newFileName).subscribe(
(rep: any[]) => {
console.log(rep[0].name);
},
err => { console.error(err); }
);
If all else fails or doesn't work for your solution you can use Type Assertion to treat rep as any[]:
this.api.rename(this.selectedFile, newFileName).subscribe(
rep => {
const responses = rep as any as any[];
console.log(responses[0].name);
},
err => { console.error(err); }
);
If the results structure is consistent across the different endpoints, it would best practice to create an interface/class to replace any[] with.
Hopefully that helps!
http.get is a asynchronous process, so you can't use for loop.
Syntactically you have to nest the gets inside forkJoin, so you have something like this. You can use the for loop to build an array of urls first.:
return Observable.forkJoin([
this.http.get(url[1]).map(res => res.json()),
this.http.get(url[2]).map(res => res.json()),
this.http.get(url[3]).map(res => res.json())
])
.map((data: any[]) => {
this.part1 = data[0];
this.part2 = data[1];
this.part3 = data[2];
});
I wonder if you may be able to do something like this. I'll have a try tomorrow. It's late..
return Observable.forkJoin(let req = [];
for(let i=0; i<file.sources.length; i++){
req[i] = this.http.get(url[i]).map(res => res.json())
}
)

Nested collection in models Sails.js [duplicate]

I've got myself a question regarding associations in Sails.js version 0.10-rc5. I've been building an app in which multiple models are associated to one another, and I've arrived at a point where I need to get to nest associations somehow.
There's three parts:
First there's something like a blog post, that's being written by a user. In the blog post I want to show the associated user's information like their username. Now, everything works fine here. Until the next step: I'm trying to show comments which are associated with the post.
The comments are a separate Model, called Comment. Each of which also has an author (user) associated with it. I can easily show a list of the Comments, although when I want to display the User's information associated with the comment, I can't figure out how to populate the Comment with the user's information.
In my controller i'm trying to do something like this:
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments') // I want to populate this comment with .populate('user') or something
.exec(function(err, post) {
// Handle errors & render view etc.
});
In my Post's 'show' action i'm trying to retrieve the information like this (simplified):
<ul>
<%- _.each(post.comments, function(comment) { %>
<li>
<%= comment.user.name %>
<%= comment.description %>
</li>
<% }); %>
</ul>
The comment.user.name will be undefined though. If I try to just access the 'user' property, like comment.user, it'll show it's ID. Which tells me it's not automatically populating the user's information to the comment when I associate the comment with another model.
Anyone any ideals to solve this properly :)?
Thanks in advance!
P.S.
For clarification, this is how i've basically set up the associations in different models:
// User.js
posts: {
collection: 'post'
},
hours: {
collection: 'hour'
},
comments: {
collection: 'comment'
}
// Post.js
user: {
model: 'user'
},
comments: {
collection: 'comment',
via: 'post'
}
// Comment.js
user: {
model: 'user'
},
post: {
model: 'post'
}
Or you can use the built-in Blue Bird Promise feature to make it. (Working on Sails#v0.10.5)
See the codes below:
var _ = require('lodash');
...
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.then(function(post) {
var commentUsers = User.find({
id: _.pluck(post.comments, 'user')
//_.pluck: Retrieves the value of a 'user' property from all elements in the post.comments collection.
})
.then(function(commentUsers) {
return commentUsers;
});
return [post, commentUsers];
})
.spread(function(post, commentUsers) {
commentUsers = _.indexBy(commentUsers, 'id');
//_.indexBy: Creates an object composed of keys generated from the results of running each element of the collection through the given callback. The corresponding value of each key is the last element responsible for generating the key
post.comments = _.map(post.comments, function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
res.json(post);
})
.catch(function(err) {
return res.serverError(err);
});
Some explanation:
I'm using the Lo-Dash to deal with the arrays. For more details, please refer to the Official Doc
Notice the return values inside the first "then" function, those objects "[post, commentUsers]" inside the array are also "promise" objects. Which means that they didn't contain the value data when they first been executed, until they got the value. So that "spread" function will wait the acture value come and continue doing the rest stuffs.
At the moment, there's no built in way to populate nested associations. Your best bet is to use async to do a mapping:
async.auto({
// First get the post
post: function(cb) {
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.exec(cb);
},
// Then all of the comment users, using an "in" query by
// setting "id" criteria to an array of user IDs
commentUsers: ['post', function(cb, results) {
User.find({id: _.pluck(results.post.comments, 'user')}).exec(cb);
}],
// Map the comment users to their comments
map: ['commentUsers', function(cb, results) {
// Index comment users by ID
var commentUsers = _.indexBy(results.commentUsers, 'id');
// Get a plain object version of post & comments
var post = results.post.toObject();
// Map users onto comments
post.comments = post.comments.map(function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
return cb(null, post);
}]
},
// After all the async magic is finished, return the mapped result
// (or an error if any occurred during the async block)
function finish(err, results) {
if (err) {return res.serverError(err);}
return res.json(results.map);
}
);
It's not as pretty as nested population (which is in the works, but probably not for v0.10), but on the bright side it's actually fairly efficient.
I created an NPM module for this called nested-pop. You can find it at the link below.
https://www.npmjs.com/package/nested-pop
Use it in the following way.
var nestedPop = require('nested-pop');
User.find()
.populate('dogs')
.then(function(users) {
return nestedPop(users, {
dogs: [
'breed'
]
}).then(function(users) {
return users
}).catch(function(err) {
throw err;
});
}).catch(function(err) {
throw err;
);
Worth saying there's a pull request to add nested population: https://github.com/balderdashy/waterline/pull/1052
Pull request isn't merged at the moment but you can use it installing one directly with
npm i Atlantis-Software/waterline#deepPopulate
With it you can do something like .populate('user.comments ...)'.
sails v0.11 doesn't support _.pluck and _.indexBy use sails.util.pluck and sails.util.indexBy instead.
async.auto({
// First get the post
post: function(cb) {
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.exec(cb);
},
// Then all of the comment users, using an "in" query by
// setting "id" criteria to an array of user IDs
commentUsers: ['post', function(cb, results) {
User.find({id:sails.util.pluck(results.post.comments, 'user')}).exec(cb);
}],
// Map the comment users to their comments
map: ['commentUsers', function(cb, results) {
// Index comment users by ID
var commentUsers = sails.util.indexBy(results.commentUsers, 'id');
// Get a plain object version of post & comments
var post = results.post.toObject();
// Map users onto comments
post.comments = post.comments.map(function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
return cb(null, post);
}]
},
// After all the async magic is finished, return the mapped result
// (or an error if any occurred during the async block)
function finish(err, results) {
if (err) {return res.serverError(err);}
return res.json(results.map);
}
);
You could use async library which is very clean and simple to understand. For each comment related to a post you can populate many fields as you want with dedicated tasks, execute them in parallel and retrieve the results when all tasks are done. Finally, you only have to return the final result.
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments') // I want to populate this comment with .populate('user') or something
.exec(function (err, post) {
// populate each post in parallel
async.each(post.comments, function (comment, callback) {
// you can populate many elements or only one...
var populateTasks = {
user: function (cb) {
User.findOne({ id: comment.user })
.exec(function (err, result) {
cb(err, result);
});
}
}
async.parallel(populateTasks, function (err, resultSet) {
if (err) { return next(err); }
post.comments = resultSet.user;
// finish
callback();
});
}, function (err) {// final callback
if (err) { return next(err); }
return res.json(post);
});
});
As of sailsjs 1.0 the "deep populate" pull request is still open, but the following async function solution looks elegant enough IMO:
const post = await Post
.findOne({ id: req.param('id') })
.populate('user')
.populate('comments');
if (post && post.comments.length > 0) {
const ids = post.comments.map(comment => comment.id);
post.comments = await Comment
.find({ id: commentId })
.populate('user');
}
Granted this is an old question, but a much simpler solution would be to loop over the comments,replacing each comment's 'user' property (which is an id) with the user's full detail using async await.
async function getPost(postId){
let post = await Post.findOne(postId).populate('user').populate('comments');
for(let comment of post.comments){
comment.user = await User.findOne({id:comment.user});
}
return post;
}
Hope this helps!
In case anyone is looking to do the same but for multiple posts, here's one
way of doing it:
find all user IDs in posts
query all users in 1 go from DB
update posts with those users
Given that same user can write multiple comments, we're making sure we're reusing those objects. Also we're only making 1 additional query (whereas if we'd do it for each post separately, that would be multiple queries).
await Post.find()
.populate('comments')
.then(async (posts) => {
// Collect all comment user IDs
const userIDs = posts.reduce((acc, curr) => {
for (const comment of post.comments) {
acc.add(comment.user);
}
return acc;
}, new Set());
// Get users
const users = await User.find({ id: Array.from(userIDs) });
const usersMap = users.reduce((acc, curr) => {
acc[curr.id] = curr;
return acc;
}, {});
// Assign users to comments
for (const post of posts) {
for (const comment of post.comments) {
if (comment.user) {
const userID = comment.user;
comment.user = usersMap[userID];
}
}
}
return posts;
});

sails.js: how to update model

Forgive my noob question. I'm using angularjs to send a user model (json) with varying fields. It works well with sails.js default PUT. I overrode the PUT, the problem is that I wish to update the model with the received JSON and do some processing on the modified model. Now I can't update the model with
User.update({
id: req.body.id
},{
req.body
}, function(err, users) {
// Error handling
if (err) {
return console.log(err);
// Updated users successfully!
} else {
console.log("Users updated:", users);
}
});
Please help
EDIT:
After knocking my head on the wall for days, problem solved! I know, my code formatting here is not the best..
changed this:
{
req.body
}
to just:
req.body
(without the braces)
full snippet becomes:
User.update({
id: req.body.id
},
req.body
, function(err, users) {
// Error handling
if (err) {
return console.log(err);
// Updated users successfully!
} else {
console.log("Users updated:", users);
}
});
Thanks.
So you figured out your problem, sort of. req.body is already an object. But you really should sanitize it before you put it into your update and then save the object. There's a lot of reasons for this but with Mongo when you get only a partial object you'll replace the object in the collection which, in your example with a user, could be bad. When I send users to the frontend I cull off things I don't want transmitted all over like passwords. The other reason is the golden rule of web application development - never trust the client! I'd start with something like:
var user = User.findOne(req.body.id).done(function(error, user) {
if(error) {
// do something with the error.
}
if(req.body.email) {
// validate whether the email address is valid?
// Then save it to the object.
user.email = req.body.email;
}
// Repeat for each eligible attribute, etc.
user.save(function(error) {
if(error) {
// do something with the error.
} else {
// value saved!
req.send(user);
}
});
});