How to get Firebase Node children - javascript

I'm pretty new to firebase on JavaScript so it's most likely a silly and possibly a duplicate question but I'm having trouble trying to get these two children from each "Task". I tried getting access of the task's properties with ["Task"] and ["Status"] but it's showing undefined for me.
var members = projectRef.child("Data").child("Members").orderByKey().startAt(firebase.auth().currentUser.uid).once('value').then(function(snapshot) // Gets all members with their data
{
var data = [];
var taskArray = [];
snapshot.forEach(function(childSnapshot) // Goes to each member
{
var childData = childSnapshot.val(); // Gets all of the member's children as an object from the childSnapshot
var childTasks = childData.Tasks;
if(childTasks)
{
console.log(childTasks);
for (task in childTasks)
{
console.log(task);
console.log(task["Status"]);
console.log(task["Task"]);
}
}
data.push(childData);
});
return data;
});
Results for childTasks
{
'-ME2IdeYYwHqWJ82ynob': { Status: 'Not completed', Task: 'flsdjlfksdjflksd' },
'-ME5YHsoMVxX5sTq5RVA': { Status: 'Not completed', Task: 'lfjsdlkfjslkdfjlksf' },
'-ME5YKh30-rs_Fq2QhBU': { Status: 'Not completed', Task: 'sflksdjfsklfjdksljsdlf' }
}
Results for the task, task["Status"], and task["Task"]
-ME2IdeYYwHqWJ82ynob
undefined
undefined
-ME5YHsoMVxX5sTq5RVA
undefined
undefined
-ME5YKh30-rs_Fq2QhBU
undefined
undefined

It looks like you're missing a forEach over the lowest level repeated node type.
projectRef.child("Data").child("Members").orderByKey().startAt(firebase.auth().currentUser.uid).once('value').then(function(snapshot) {
snapshot.forEach(function(memberSnapshot) {
var tasksSnapshot = memberSnapshot.child("Tasks");
tasksSnapshot.forEach(function(taskSnapshot) {
console.log(taskSnapshot.val());
console.log(taskSnapshot.child("Status").val());
})
});
});
Things I changed that I'd highly recommend:
Stick to navigating snapshots as long as possible, before calling val().
Give your snapshots meaningful names, representing the entities in your JSON.
In general though, it seems you initial query can be replaced by a direct lookup, since it seems you want to look up the member by their UID and it's unique. By looking up the user (and their Tasks node( directly, you can remove another forEach loop, making the code even easier to read:
var memberRef =
projectRef.child("Data").child("Members").child(firebase.auth().currentUser.uid);
memberRef.once('value').then(function(memberSnapshot) {
var tasksSnapshot = memberSnapshot.child("Tasks");
tasksSnapshot.forEach(function(taskSnapshot) {
console.log(taskSnapshot.val());
console.log(taskSnapshot.child("Status").val());
})
});
And since you only use the Tasks child node, you can simplify this even further to:
var memberRef =
projectRef.child("Data").child("Members").child(firebase.auth().currentUser.uid);
var tasksRef = memberRef.child("Tasks");
tasksRef.once('value').then(function(tasksSnapshot) {
tasksSnapshot.forEach(function(taskSnapshot) {
console.log(taskSnapshot.val());
console.log(taskSnapshot.child("Status").val());
})
});

Related

How to update array inside object, inside array mongoDB

So the document contains an array of objects, each object containing it's own array. So how would I go about updating one of the elements in the array that's inside the object which is inside another array. I've read some things with $. But I don't understand completely how to use it to call a position. I know the position of the element. But I can't just say $[] because the position is defined in a variable and not a string...
I've tried doing a simple
db.collection.findOne({...}, (err, data) => {...});
and then changing the arrays in the objects in the array in there with a simple:
data.arr[x].type[y] = z; data.save().catch(err => {console.log(err)});
But it doesn't save the new values I set for for the element of the array.
Sample structure after proposed solution from #Tom Slabbaert:
Data.findOne({
userID: 'CMA'
}, (err, doc) => {
if(err) {console.log(err)}
if(doc) {
for(var i = 0; i<CMA.stockMarket.length; i++) {
if(CMA.stockMarket[i].name == data.userID) {
for(var z = 0; z<CMA.stockMarket[i].userStock.length; z++) {
if(z == company) {
var updateAmount = CMA.stockMarket[i].userStock[z]+args[1]
var updateKey = `stockMarket.${i}.userStock.${z}`
Data.updateOne({userID: 'CMA'}, {'$set': {[updateKey]: updateAmount}})
}
}
}
}
}
});
-------------------------EDIT-------------------------
So I tried changing some things around in the data base to see if that would fix the problem I was having. I modified the updated code that was provided by #Tom Slabbaert. But nothing seems to work for some reason :/ Here's what I have so far, at this point I hope it's just a syntax error somewhere. Cause this is really frustrating at this point. Note that I'm still using the for loops here to find if the info exists. And if not, push that info into the database. This might only be temporary until I find a better way / if there is a better way.
for(var i = 0; i<CMA.userStocks.length; i++) {
if(CMA.userStocks[i].name == data.userID) {
for(var z = 0; z<CMA.userStocks[i].shares.length; z++) {
//console.log(CMA.userStocks[i].shares[z].companyName)
if(CMA.userStocks[i].shares[z].companyName == args[0]) {
var updateKey = `CMA.userStocks.$[elem1].shares.$[elem2].amount`
Data.updateOne(
{userID: 'CMA'},
{
"$inc": {
[updateKey]: args[1]
}
},
{
arrayFilters: [
{
"elem1.name": data.userID,
"elem2.companyName": args[0]
}
]
}
)
purchaseComplete(); return;
}
}
CMA.userStocks[i].shares.push({companyName: args[0], amount: parseInt(args[1])})
CMA.save().catch(err => {console.log(err)});
purchaseComplete(); return;
}
}
CMA.userStocks.push({name: data.userID, shares: [{companyName: args[0], amount: parseInt(args[1])}]});
CMA.save().catch(err => {console.log(err)});
purchaseComplete(); return;
The data I'm trying to find and change is structured like the following:
And what I'm trying to change in the end is the 'amount' (which is an integer)
_id: (Not relavent in this question)
userID: 'CMA'
stockMarket: [...] (Not relavent in this question)
userStocks: [
Object: (position 0 in userStocks array)
name: 'string' (equal to data.userID in the code)
shares: [
Object: (position 0 in shares array)
companyName: 'string' (this is args[0] in the code)
amount: integer
]
]
You can just prepare the "key" ahead of time. like so:
const updateKey = `arr.${x}.type.${y}`
db.collection.updateOne(
{...},
{
"$set": {
[updateKey]: z
}
})
Mongo Playground
Using Mongo's positional operators ($ and $[]) are usually required when you don't know the position in the array and want to use a condition to update the element.
------ EDIT-----
After given your sample code you just have a minor syntax error:
var updateKey = `stockMarket.${i}.userStock.${z}`
Should just be:
var updateKey = `CMA.stockMarket.${i}.userStock.${z}`
However After seeing your code I recommend you execute the following solution which uses a single update with arrayFilters, it just cleans up the code quite a bit:
const updateKey = `CMA.stockMarket.$[elem1].userStock.${company}`;
db.collection.update(
{userID: 'CMA'},
{
"$inc": {
[updateKey]: args[1]
}
},
{
arrayFilters: [
{
"elem1.name": data.userID
}
]
})
Mongo Playground
Well I found something that worked. Apparently it didn't save the db.collection.updateMany unless I made a .then() function on the end? I have no idea why, but it's the same with an aggregate I made. (It basically does the same as a Data.findOne and save it too, but it isn't limited by the parallel save error)
Solution I found with aggregation:
<collection field> = <new data for collection field>
Data.aggregate([
{
$match: { //This is used to create a filter
['<insert field>']: <insert filter>
}
}, {
$addFields: { //This is used to update existing data, or create a new field containing the data if the field isn't found
['<collection field>']: <new data for collection field>
}
}, {
$merge: { //This is used to merge the new data / document with the rest of the collection. Thus having the same effect as a standard save
into: {
db: '<insert database name>',
coll: '<insert collection name>'
}
}
}
]).then(() => {
//After it's done, do something here. Or do nothing at all it doesn't matter as long as the .then() statement remains. I found that not having this part will break the code and make it not save / work for some reason.
}); return;
Solution I found with db.collection.updateMany
db.collection.updateMany(
{<insert field>: filter}, {$set: {'<insert field>': <new data>}}
).then(() => {
//This .then() statment in my case was needed for the updateMany function to work correctly. It wouldn't save data without it for some reason, it does not need to contain any actual info in this part. As long as it's here.
});
With this new info I could simply access and change the data that I was trying to before using the previous instructions provided by #Tom Slabbaert and my new method of actually making it save the changes made into the document.

How to crawling using Node.js

I can't believe that I'm asking an obvious question, but I still get the wrong in console log.
Console shows crawl like "[]" in the site, but I've checked at least 10 times for typos. Anyways, here's the javascript code.
I want to crawl in the site.
This is the kangnam.js file :
const axios = require('axios');
const cheerio = require('cheerio');
const log = console.log;
const getHTML = async () => {
try {
return await axios.get('https://web.kangnam.ac.kr', {
headers: {
Accept: 'text/html'
}
});
} catch (error) {
console.log(error);
}
};
getHTML()
.then(html => {
let ulList = [];
const $ = cheerio.load(html.data);
const $allNotices = $("ul.tab_listl div.list_txt");
$allNotices.each(function(idx, element) {
ulList[idx] = {
title : $(this).find("list_txt title").text(),
url : $(this).find("list_txt a").attr('href')
};
});
const data = ulList.filter(n => n.title);
return data;
}). then(res => log(res));
I've checked and revised at least 10 times
Yet, Js still throws this result :
root#goorm:/workspace/web_platform_test/myapp/kangnamCrawling(master)# node kangnam.js
[]
Mate, I think the issue is you're parsing it incorrectly.
$allNotices.each(function(idx, element) {
ulList[idx] = {
title : $(this).find("list_txt title").text(),
url : $(this).find("list_txt a").attr('href')
};
});
The data that you're trying to parse for is located within the first index of the $(this) array, which is really just storing a DOM Node. As to why the DOM stores Nodes this way, it's most likely due to efficiency and effectiveness. But all the data that you're looking for is contained within this Node object. However, the find() is superficial and only checks the indexes of an array for the conditions you supplied, which is a string search. The $(this) array only contains a Node, not a string, so when you you call .find() for a string, it will always return undefined.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
You need to first access the initial index and do property accessors on the Node. You also don't need to use $(this) since you're already given the same exact data with the element parameter. It's also more efficient to just use element since you've already been given the data you need to work with.
$allNotices.each(function(idx, element) {
ulList[idx] = {
title : element.children[0].attribs.title,
url : element.children[0].attribs.href
};
});
This should now populate your data array correctly. You should always analyze the data structures you're parsing for since that's the only way you can correctly parse them.
Anyways, I hope I solved your problem!

How to check if key is not found in IndexedDB?

On the indexeddb i want to look if there is a key permanent and do some actions. But if not, i want to make some other actions. I can do the actions if the permanent is there, however when it is not I can get the onerror to work. Is the onerror suppose to do this thing? How can I check if there is not value in it?
var hashtype = 'permanent';
var getPermanent = store.get(hashtype);
getPermanent.onsuccess = function() {
var ivrame = getPermanent.result.value;
};
getPermanent.onerror = function() {
console.log('onerror')
};
See the note under https://w3c.github.io/IndexedDB/#dom-idbobjectstore-get - the get method yields success with undefined if there is no matching record.
So you have a few options:
Use get(key) and test the result for undefined. This works unless undefined is a value you expect to store (it's a valid value)
Use count(key) - the result will be 1 if present, 0 if absent. Easy if you're just testing for existence, but doesn't get you the record.
Use openCursor(key) and test to see if the request's result is a cursor (record present as request.result.value) or undefined (no record in range)
For your code:
var hashtype='permanent';
// #1: Use get
var getPermanent = store.get(hashtype);
getPermanent.onsuccess = function() {
if (getPermanent.result === undefined) {
// no record with that key
} else {
var value = getPermanent.result;
}
};
// #2: Use count
var getPermanent = store.count(hashtype);
getPermanent.onsuccess = function() {
if (getPermanent.result === 0) {
// no record with that key
} else {
...
}
};
// #3: Use cursor
var getPermanent = store.openCursor(hashtype);
getPermanent.onsuccess = function() {
var cursor = getPermanent.result;
if (!cursor) {
// no record with that key
} else {
var value = cursor.value;
}
};
The function assigned to request.onsuccess is a callback function that is always called, regardless of whether the value is present in the store. When there is no corresponding object in the store, the result object will be undefined. When there is a corresponding object in the store, the result object will be defined. So you simply need to check if the object is defined from within the onsuccess callback function.
request.onerror is a separate callback from request.onsuccess. onerror gets called when there is some type of failure in indexedDB (e.g. something like you tried to get a value from a store that doesn't exist, or you tried to put a duplicate object into a store that doesn't permit duplicates). request.onerror does not get called when no value is found as a result of calling store.get, because that is not considered an 'error' in the failure sense.
So, what you want to do is something like this:
var hashtype='permanent';
var getPermanent = store.get(hashtype);
getPermanent.onsuccess = function(event) {
//var ivrame=getPermanent.result.value;
var result = getPermanent.result;
if(result) {
console.log('Got a result!', result);
var ivrame = result;
} else {
console.log('Result was undefined! No matching object found');
}
};
getPermanent.onerror = function() {
console.log('Something went wrong trying to perform the get request');
};
Do not try and access request.result.value. There is no such thing in the case of a get request. When using store.get, request.result contains the matching object you want, or is undefined. When using store.openCursor, request.result contains the cursor, which is defined if there is at least one matching object and you have not already iterated past it. To get the matching object at the cursor's current position, you would use cursor.value. Here, cursor.value will always be defined, because cursor would otherwise be undefined, and you would obviously check for that beforehand.
Instead of using getPermanent.result to access data provided by 'get' request it is better to use event.target.result. It also can be compared with undefined to check absence of requested key:
db = this.result;
var tr = db.transaction("data");
var objstore = tr.objectStore("data");
var getres = objstore.get(0);
getres.onsuccess = function(event)
{
if(event.target.result.data === undefined)
console.log("key not found");
}

Dynamically adjust object names based on parameter

I'm running a script on an apache webserver on a linux box. Based on the parameter I want to change the name of variable(or set it)
The idea is that humDev(lines 11 and 14) is named humDev21 for example. Where devId is the number 21 in this example.
My script looks like this:
function getHumDev(devId){
$.ajax({
async: false,
url: "/url" + devId,
success: function(result) {
var array = result["Device_Num_" + devId].states;
function objectFindByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
humDev = array[i].value;
}
}
return humDev;
};
objectFindByKey(array, 'service', 'some');
}
});
};
If Im looking in the wrong direction, please do let me know. Maybe its bad practice what Im trying. The reason I want to have the object a unique name is because this function is called several times by another function, based on the content of an array. But when I have the humDev object named without the number suffix to make it unique, the content of the object is getting mixed up between the different calls.
I may be off base but I am making some assumptions based on what I understand of what you are trying to do.
First, you need to understand how to do file I/O in node.js. So lets start there:
var pathToFile, //set with file path string
fs = require('fs'), //require the file i/o module API
bunchOfHumDevs = {},
fileContents; //we'll cache those here for repeated use
fs.readFile(pathToFile, function(err, result) {
if (err) {
throw new Error(); //or however you want to handle errors
} else {
fileContents = JSON.parse(result); //assumes data stored as JSON
}
});
function getHumDev(devId) {
//first make sure we have fileContents, if not try again in 500ms
if (!fileContents) {
setTimeout(function() {
getHumDev(devId);
}, 500);
} else {
var array = fileContents["Device_Num_" + devId].states,
i = array.length,
//if 'service' and 'some' are variable, make them params of
//getHumDev()
while (i--) {
if (array[i]['service'] === 'some') {
//store uniquely named humDev entry
bunchOfHumDevs['humDev' + devId.toString()] = array[i].value;
break; //exit loop once a match is found
}
}
}
return null;
}
getHumDev(21);
assuming a match is found for the devId 21, bunchOfHumdevs will now have a property 'humDev21' that is the object (value?) in question. Also, the fileContents are now cached in the program so you don't have to reopen it every time you call the function.

Why is hashtagseen[] empty after I call the addposthashtags function?

I am trying to add hashtags in the post's hashtag[] array as a object with a num:1 variable to the users hashtagseen[] array if it is not already in it else add 1 the num if the hashtag is already in the hashtagseen[] array. How do I fix my code? Here is the code, thanks in advanced.
edit: I think I am not finding post.hashtag with this.hashtag and that is why it will not go to else. Just a guess.
The user object
Accounts.createUser({
username: username,
password: password,
email: email,
profile: {
hashtagsl:[],
}
});
collections/post.js
var post = _.extend(_.pick(postAttributes, 'title', 'posttext','hashtags'), {
userId: user._id,
username: user.username,
submitted: new Date().getTime(),
commentsCount: 0,
upvoters: [], votes: 0,
});
calling it
Meteor.call('addposthashtags',this.hashtags,Meteor.user().profile.hashtagsl);
lib/usershash
Meteor.methods({
addposthashtags: function (hashtags,hashtagsl) {
//supposed to make hashtagseen a array with the names from the hashtagsl object in it
var hashtagseen = _.pluck(hashtagsl, 'name');
//supposed to run once per each hashtag in the posts array.
for (var a = 0; a < hashtags.length; a++) {
//supposed set hashtagnumber to the number indexOf spits out.
var hashnumber=hashtagseen.indexOf(hashtags[a]);
//supposed to check if the current hashtag[a] === a idem in the hashtagseen.
if(hashnumber===-1){
var newhashtag = this.hashtags[a];
//supposed to make the object with a name = to the current hashtags
Meteor.users.update({"_id": this.userId},{"$push":{"profile.hashtagsl": {name: newhashtag, num: 1}}})
} else {
var hashi = hashtagseen[hashnumber];
//supposed to ad one to the num variable within the current object in hashtagsl
Meteor.users.update({"_id": this.userId, "profile.hashtagsl.name":hashi},{"$inc":{"profile.hashtagsl.num":1}});
}
}
}
});
Your addposthashtags function is full of issues. You also haven't provided a "schema" for hashtag objects.
addposthashtags: function () {
for (a = 0; a < this.hashtag.length; a++) {
// Issue1: You're querying out the user for every iteration of the loop!?
for (i = 0; i < Meteor.user().profile.hashtagseen.length; i++) {
// Issue2: You're comparing two _objects_ with ===
// Issue3: Even if you use EJSON.equals - the `num` property wont match
// Issue4: You're querying out the user again?
if (this.hashtag[a] === Meteor.user().profile.hashtagseen[i]) {
// Issue5 no `var` statement for hashtagseeni?
// Issue6 You're querying out the user again??
hashtagseeni = Meteor.user().profile.hashtagseen[i];
//Issue7 undefined hashtagsli?
//Issue8 Calling multiple methods for the one action (eg in a loop) is a waste of resources.
Meteor.call('addseen', hashtagsli);
} else {
//Issue9 no `var` statement for newhashtag?
newhashtag = this.hashtag[a];
newhashtag.num = 1;
//Issue8b Calling multiple methods for the one action (eg in a loop) is a waste of resources.
Meteor.call('updateUser', newhashtag, function (err, result) {
if (err)
console.log(err);
});
}
}
}
}
Also, the method has similiar issues:
addseen: function (hashtagseeni) {
// Issue10: var `profile` is undefined
// Issue11: should use `this.userId`
// Issue12: hashtagseeni wouldn't match profile.hashtagseen due to "num" field.
Meteor.users.update({"_id": Meteor.userId, "profile.hashtagseen": profile.hashtagseeni}, {"$inc":{"profile.hashtagseen.$.num":1}});
}
New issues with your new set of code:
Meteor.methods({
addposthashtags: function (hashtags,hashtagsl) {
//Issue1 `hashtag` is undefined, guessing you mean `hashtags`
//Issue2 no `var` for a
for (a = 0; a < hashtag.length; a++) {
//Issue3 no `var` for i
//Issue4 Why are you looping through both?
// don't you just want to check if hashtag[a] is in hashtagsl?
for (i = 0; i < hashtagsl.length; i++) {
if (hashtags[a] === hashtagsl[i].name) {
var hashi = hashtagsl[i].name;
//supposed to ad one to the num variable within the current object in hashtagsl.
// Issue5: This query wont do what you think. Test until you've got it right.
Meteor.users.update({"_id": Meteor.userId, 'profile.hashtagsl':hashi}, {"$inc":{"num":1}});
} else {
// Issue6 `this.hashtag` isn't defined. guessing you mean `hashtags[a]`
var newhashtag = this.hashtag[a];
// Issue7 superfluous statement
var newhashtagnum = num = 1;
// Issue8 Obvious syntax errors
// Perhaps try Meteor.users.update({"_id": this.userId},{"$push":{"profile.hashtagsl": {name: newhashtag, num: 1}}})
Meteor.users.update({"_id": Meteor.userId, 'profile'},{"$addToSet":{"hashtagsl"[newhashtag]=newhashtagnum}})
};
};
};
};
});
I'd suggest you trying the following
1) Assuming that after newhashtag=hashtag[a] you get a JSON object in newhashtag variable, try replacing newhashtag:{num:1}; with newhashtag.num = 1 - this will add the num variable to the object and set the value.
1.a) For debugging purposes try adding some console.log(JSON.stringify(newhashtag)); after each of the two lines where you're setting and changing the newhashtag variable - this way you'll know exactly what you're trying to add to the mongoDB document.
2) The update to increment the views also doesn't seem to me that will work. Couple of things to note here - $set:{'profile.hashtagseen[i]':num++} - MongoDB won't be able to identify the 'i' in 'profile.hashtagseen[i]' and 'num++' is not how increments are done in Mongo.
I'd suggest you look into the $inc and to the positional update documentation of MongoDB.
Your final increment update statement will look something like
Meteor.users.update({"_id": Meteor.userId, "profile.hashtagseen": profile.hashtagseen[i]}, {"$inc":{"profile.hashtagseen.$.num":1}});
I see that executing addposthashtags is in the client, and you must to pay attention because this function will execute in minimongo and doesn't work all operations. First you try execute this operation under mongo if it's work you must to create one function inside the folder server.
Add text of the documentation of Minimongo
In this release, Minimongo has some limitations:
$pull in modifiers can only accept certain kinds of selectors.
findAndModify, aggregate functions, and map/reduce aren't supported.
All of these will be addressed in a future release. For full Minimongo
release notes, see packages/minimongo/NOTES in the repository.
Minimongo doesn't currently have indexes. It's rare for this to be an
issue, since it's unusual for a client to have enough data that an
index is worthwhile.
You try create one method on the server, with the same operation.
Server:
Meteor.methods({
updateUser: function (newhashtag) {
Meteor.users.update(this.userId,
{
$addToSet: {'profile.$.hashtagseen': newhashtag}
});
}
});
Client:
Meteor.call('updateUser',newhashtag,function(err,result){
if (err)
console.log(err);// there you can print the erro if there are
});
Minimongo doesn't support alls operation, for test you can to execute in the console for testing the method if supported. After that you can to execute the operation under mongo directly, that clears your doubts.

Categories