I have a list of users, which contains a node called scannedCards, which contains an array of cardIds. Below is a screenshot of one user.
There are multiple users like above, I want to delete all the card Ids if they are equalTo(Some Card Id), I want to this with all the users at once.
Below is the query I a using:
const ref = database.ref('users');
const query = ref.orderByChild('cardId').equalTo(cardId);
But the result is always empty, Can someone please tell how I can achieve this?
The query you listed above doesn't seem to be returning anything because, based on the screenshot provided, cardId isn't a direct child property of users, but rather a property of each object under the scannedCards list that users may have.
If you only need to do this as part of a one-time operation, you may run something like this:
const ref = database.ref('users');
ref.once('value').then(snapshot => {
const users = snapshot.val();
for (var user_id in users) {
const scannedCards = users[user_id].scannedCards;
for (var card in scannedCards) {
if (scannedCards[card].card_id == "<some card id>") {
firebase.database().ref(`users/${user_id}/scannedCards/${card_id}`)
.set(null);
}
}
}
});
If you plan on letting logged-in users delete their scanned cards, it would be a little bit less complicated:
// user_id is for whoever is logged in
const ref = firebase.database().ref(`users/${user_id}/scannedCards`);
ref.once('value').then(snapshot => {
const scannedCards = snapshot.val();
for (var card in scannedCards) {
if (scannedCards[card].card_id == "<some card id>") {
firebase.database().ref(`users/${user_id}/scannedCards/${card_id}`)
.set(null)
}
}
});
Also worth pointing out, scannedCards is not an array of card ids, because the keys are object ids and the values are objects (which contain cardId and userId properties).
Related
I am not very efficient with my code which may be the reasons why this keeps failing. I am trying to remove and assign roles to "verified" users. The basic gist of the code is to loop through all "verified" users and assign them appropriate roles according to the data received from the API.
const fetch = require("node-fetch");
var i = 0;
function mainLoop(
guild,
redisClient,
users,
main_list,
Pilot,
Astronaut,
Cadet,
main_guild,
cadet_guild,
guest
) {
setTimeout(function () {
redisClient.GET(users[i], async function (err, reply) {
if (reply != null) {
var json = await JSON.parse(reply);
var uuid = Object.keys(json).shift();
if (Object.keys(main_list).includes(uuid)) {
var tag = users.shift();
var rank = main_list[uuid];
console.log(`${tag}: ${rank}`);
var role = guild.roles.cache.find(
(role) => role.name === `| ✧ | ${rank} | ✧ |`
);
await guild.members.cache.get(tag).roles.remove(guest);
await guild.members.cache.get(tag).roles.remove(Astronaut);
await guild.members.cache.get(tag).roles.remove(Cadet);
await guild.members.cache.get(tag).roles.remove(Pilot);
await guild.members.cache.get(tag).roles.remove(cadet_guild);
await guild.members.cache.get(tag).roles.add(main_guild);
await guild.members.cache.get(tag).roles.add(role);
} else {
var tag = users.shift();
console.log(`${tag}: Guest`);
await guild.members.cache.get(tag).roles.remove(Astronaut);
await guild.members.cache.get(tag).roles.remove(Cadet);
await guild.members.cache.get(tag).roles.remove(Pilot);
await guild.members.cache.get(tag).roles.remove(main_guild);
await guild.members.cache.get(tag).roles.remove(cadet_guild);
await guild.members.cache.get(tag).roles.add(guest);
}
}
i++;
if (i < users.length) {
mainLoop(
guild,
redisClient,
users,
main_list,
Pilot,
Astronaut,
Cadet,
main_guild,
cadet_guild,
guest
);
}
});
}, 5000);
}
The code will fetch api data, map the "verified" users and api data into an array. Then, when it starts looping through the users array, it will only log 3 times and not assign any roles. Any help would be appreciated.
I can provide extra explanation/code if needed.
One possible issue I see here is that you are both incrementing the index i and calling .shift() on the users array. This may be the cause of the problem you are experiencing, as this will entirely skip some of the users in the array. Array.shift() doesn't just return the first element of the array; it removes it from the array.
Consider, for example, that your users array looks like this:
var users = ["Ted", "Chris", "Ava", "Madison", "Jay"];
And your index starts at 0 like so:
var i = 0;
This is what is happening in your code:
Assign roles for users[i]; the index is currently 0, so get users[0] (Ted).
Get Ted's tag via users.shift(). users is now: ["Chris", "Ava", "Madison", "Jay"]
Increment the index with i++. i is now: 1.
Assign roles for users[i]; the index is currently 1, so get users[1] (now Ava, skips Chris entirely).
Get Ava's tag via users.shift() (actually gets Chris' tag). users is now: ["Ava", "Madison", "Jay"]
Increment the index with i++. i is now: 2.
Assign roles for users[i]; the index is currently 2, so get users[2] (now Jay, skips Madison entirely).
And so on, for the rest of the array; about half of the users in the users array will be skipped.
I don't know how many users are supposed to be in your users array, but this could be the reason why so few logs are occurring. Note, however, that this is just one cause of the problem you are experiencing; it is possible that there are more reasons why you are having that issue, such as rate limits.
My recommendation on how to fix this is to not use users.shift() to get the user's tag. Simply use users[i], which will return the proper tag value without messing with the length of the array. Another way to fix this would be to remove the index incrementation, and always use 0 as your index. Use one or the other, but not both.
The goal is to filter an array based on the slots the user has selected.
For example an array has slots for 7pm-9pm,10pm-12pm and so on.
Now the user selects 7pm-9pm, so now I want to filter the array which have 7ppm-9pm or is the users wants
7pm-9pm and 10pm-11pm so the data should be based on 7pm-9pm and 10pm-11pm
Here is how I store the values
This is the original array
data :[
{
name:"something",
phone:"another",
extraDetails : {
// some more data
slots : [
{item:"6PM-7PM"},
{item:"7PM-8pm}
]
}
},{
// Similarly more array with similar data but somewhere slots might be null
}
]
Now for example we have this array
slots:[{6PM-7PM,9PM-10PM,11PM-12AM}]
Now this should filter all those which includes timeslots of 6PM-7PM,9PM-10PM,11PM-12AM
or if the user selects
slots:[{6PM-7PM}]
We should still get the results that includes 6pm-7pm more or else don't matter.
First, I'd suggest using this for your slots representation for simplicity, but you can alter this approach depending on your actual code:
slots: ['6PM-7PM', '9PM-10PM', '11PM-12PM']
Then you can iterate through your data and use filter:
const querySlots = ['6PM-7PM', '9PM-10PM', '11PM-12PM'];
const matchedPersonsWithSlots = data.filter( (person) => {
let i = 0;
while ( i < person.extraDetails.slots.length ) {
if (querySlots.includes(person.extraDetails.slots[i]) return true;
i += 1;
}
return false;
});
matchedPersonsWithSlots will then have all the people that have a slot that matches one of the slots in your query, because if any of the query slots are in a person's list of slots, then it's included in the result set.
EDIT to include a different use case
If, however, every slot in the query array must be matched, then the filtering has to be done differently, but with even less code.
const matchedPersonsWithAllSlots = data.filter(person =>
querySlots.every((qSlot)=>person.extraDetails.slots.includes(qSlot)));
The above will go through each person in your data, and for each of them, determine whether the person has all of your query slots, and include them in the result list, only if this is true.
var Players = message.guild.roles.cache.get('roleID1','roleID2','roleID3','roleID4','roleID5','roleID6','roleID7').members.map(m=>m.user.tag);
^Right now, this line of code for my discord bot retrieves a list of players that have ALL of the roles with those role IDs. It then dumps this player list into an array named 'Players'
Instead of having it look for players that have ALL of those roles, how can I change this line to instead look for players that have ANY ONE of those roles? Or do I have to actually search for individual roles multiple times and add them to the array for a munch longer, uglier code?
Unfortunately, you can pass only one parameter to the Collection.get() method. Your code will only get the members of the role roleID1.
To go around this you can make an array with all the role IDs you want to get and use Collection.filter():
const RolesArray = ["roleID1", "roleID2", "roleID3", "roleID4", "roleID5", "roleID6", "roleID7"];
const Roles = Guild.roles.cache.filter(role => RolesArray.some(roleValue => roleValue == role.id));
Roles is now a Collection (just like Guild.roles.cache) containing the roles in RolesArray.
Now you can map the roles by members and then map members by ID.
const Members = Roles.map(role => role.members.map(member => member.user.tag));
// It will return an array with each member for each role.
// --> [["Role1Member", "Role1Member"], ["Role2Member", "Role2Member]];
Now, we can create an array of members and add every member in it. But if a member has 2 or more roles, their tag will be duplicated. So we'll make sure to remove it.
const RolesArray = ["roleID1", "roleID2", "roleID3", "roleID4", "roleID5", "roleID6", "roleID7"];
const Roles = Guild.roles.cache.filter(role => RolesArray.some(roleValue => roleValue == role.id));
const Members = [];
// This will be our final result, without duplicates.
let UniqueMembers = [];
// Looping through each role and the members of the role and adding them to the Members array.
Roles.map(role => role.members.map(member => member.user.tag)).forEach(memberArray => {
memberArray.forEach(member => {
Members.push(member);
});
});
// Removing the duplicates.
UniqueMembers = Members.filter(function(item, position) {
return Members.indexOf(item) == position;
});
// Logging our final result, without duplicates.
console.log(UniqueMembers);
So I am trying to delete the field Notes[1], selectedNote has a value of the selected array I need to delete.
window.addEventListener("load", function load(event) {
document.getElementById("deleteNotes").onclick = function() {
console.log("you did click atleast");
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
let user = firebase.auth().currentUser;
let userInfo = db.collection("Users").doc(user.uid);
userInfo.get().then(function(doc) {
if (doc.exists) {
let selectedNote = document.getElementById("noteSelect")
.selectedIndex;
console.log(selectedNote);
var cityRef = db.collection("Users").doc(user.uid);
cityRef.update({
Notes: FieldValue.delete().arrayRemove(selectedNote)
});
}
});
}
});
};
});
So I am trying to use this
cityRef.update({
Notes: FieldValue.delete().arrayRemove(selectedNote)
});
to delete the selectedNote which is the array 1 for example inside of Notes. I don't want the entire Notes field deleted but just the selected array inside the Notes field. For some reason I am struggling to get it working. Any help would be appreciated <3
Firebase arrays doesn't use a indexes for their arrays, instead they require the value of the array entry. this does mean you can't get away with duplicated data.
you will have to fetch the array "Notes" first and return its value from its index
Although, I have heard that the arrays can come back out of order because they don't rely on an index.
change this line;
Notes: FieldValue.delete().arrayRemove(selectedNote)
to this;
Notes: FieldValue.delete().arrayRemove(doc.data().Notes[selectedNote])
I would recommend you pull the value of the array from a stored variable locally to ensure the values match pre and post edit.
Let say I have these 2 fields in my firebase database.
user: {
postCount: 2
posts: [
{title: hello, content: world},
{title: hello again, content: world}
]
}
I want the user to have permission to update his posts. but I don't want him to be able to update his post count. I want the post counts to always represent the number of posts and prevent the user from cheating it.
How can I do this in firebase? Is it possible with front end javascript only? If not what would be the option that requires the least server side code possible?
This is the code I'm using but it doesn't prevent users from cheating and just calling the increment function by themselves infinite times.
const push = (objectToInsert, firebasePath) => {
const key = firebase.database().ref().child(firebasePath).push().key
let updates = {}
updates[firebasePath + key] = objectToInsert
firebase.database().ref().update(updates)
}
const increment = (firebasePath) => {
const ref = firebase.database().ref(firebasePath)
ref.transaction( (value) => {
value++
return value
})
}
push(post, `/${user}/${posts}/`)
increment(`/${user}/${postCount}`)
Referring you to firebase rules:
https://firebase.google.com/docs/database/security/#section-authorization
Either you set the rules on each of the user properties based on your security setup and keep the structure as you mentioned, Or move the counts to another node and set the rules (ex. user_post_counts).