I have the following database setup in Firebase:
I am trying to write a script that searches within media (in all users) and returns a match if any particular media url matches the url argument I pass through. And if a match is returned I would like to remove this media key/node. However, my script is just returning null. Any ideas?
const ref = firebase.database().ref('/users/');
const url = 'https://XXXXXX.s3.amazonaws.com/profiles/william/1478471907-me.jpg';
return ref.child('media').orderByChild('url').equalTo(url).once("value").then(function(snapshot) {
console.log(JSON.stringify(snapshot.val()));
});
Ok thanks to Imjared's feedback I've found a solution - including a way to remove the node:
// reference the media, B4K... could be an variable to target the correct key
const ref = firebase.database().ref('/users/B4KWeemv78R2GP7Jqul2f70kVM73/media');
return ref.orderByChild('url').equalTo(url).once("value").then(function(snapshot) {
// get the key of the respective image
const key = Object.keys(snapshot.val())[0];
// delete image node from firebase
ref.child(key).remove();
return {
success: 1,
message: 'Image deleted.'
};
}
Related
I am building a chatbot on Dialogflow and using Firestore as my database. I was originally using a custom auto ID generator, but I am now trying to use Firestores in built one, and getting errors with my current code.
function AddWaterConsumption (agent) {
// Get parameter from Dialogflow with the string to add to the database
var databaseEntry = {
"PatientID": agent.parameters.Patient_ID,
"DailyConsumption": agent.parameters.Water_consumption_user,
"DailyWaterPlan": agent.parameters.Daily_water_plan,
};
let Patient_ID = agent.parameters.Patient_ID;
console.log(`Okay, we're trying to write to database: ${databaseEntry}`);
// here is where changes are needed
const dialogflowAgentRef = db.collection("WaterConsumption").doc(genRand(8)); // need to change to add()
console.log(`Found agent ref: ${dialogflowAgentRef}`);
return db.runTransaction(t => {
t.set(dialogflowAgentRef, databaseEntry); // this will also need to be changed
return Promise.resolve('Write complete');
}).then(doc => {
agent.add(`I have updated your daily water consumption`);
agent.add(`Is anything else I can do for you?.`);
}).catch(err => {
console.log(`Error writing to Firestore: ${err}`);
agent.add(`Failed to write "${dialogflowAgentRef}" to the Firestore database.`);
});
}
I have changed the important lines to:
console.log(`Okay, we're trying to write to database: ${databaseEntry}`);
const dialogflowAgentRef = db.collection("WaterConsumption"); // changed here
console.log(`Found agent ref: ${dialogflowAgentRef}`);
return db.runTransaction(t => {
t.set(db.collection("WaterConsumption").add(databaseEntry), databaseEntry); // and here
Which does successfully write to the db and auto generates an ID. Although an error is being caught:
Argument "documentRef" is not a valid DocumentReference. Input is not a plain JavaScript object
This makes no sense:
t.set(db.collection("WaterConsumption").add(databaseEntry), databaseEntry);
In this line the db.collection("WaterConsumption").add(databaseEntry) is a call to Firestore to create a document outside of the transaction). What you want instead is to just create a new DocumentReference with a unique ID, which (as shown in the 3rd snippet in the documentation on adding documents) can be done with doc().
So:
const newDocRef = db.collection("WaterConsumption").doc();
t.set(newDocRef, databaseEntry);
So basically what I am trying is, access a document throught a reference attribut that is written in an other document like this:
Screen of the collection and the document that contains the references in an array
Here is the javascript function with which I would like to access the document. In this example I just return the reference attribut to see what it contains:
exports.importProducts = functions.https.onRequest(async (request, response) => {
const res = { success: false, error: "", docrefs: []};
const groceryListID = "groceryList1";
try {
const groceryListDoc = await admin
.firestore()
.collection("GroceryList")
.doc(groceryListID.toString())
.get();
if (!groceryListDoc.exists) {
res.error =
"Grocery list could not be found";
response.send(res);
// return res;
}
else {
const docref = groceryListDoc.data().docReferences;
res.success = true;
res.docrefs = docref[0];
response.send(res);
}
}
catch (e) {
res.error = "Error while reading the Grocery List document : " + e;
response.send(res);
//return res;
}
});
Here is the result I get when im reading the reference attribut:
Result picture
Result in text format: {"success":true,"error":"","docrefs":{"_firestore":{"projectId":""},"_path":{"segments":["Products","p1"]},"_converter":{}}}
I know that I could parse the array elements of the "segments" to get the path and access to the document but is there a better way manage that? Maybe thanks a DocumentReference object?
The value that the SDK gives you back for a DocumentReference field type in the database is actually a DocumentReference object from the SDK too. So you can just call get() on the value you get, to get a snapshot of the references document data.
I am making a bot using Discord.js and it only needs to track messages in a certain channel, which I currently have this hard-coded for testing purposes.
var { channelID } = require(`./config.json`);
bot.on("message", async (message) => {
const args = message.content.split(/ +/g);
if (message.channel.id === channelID) {
// ...
}
});
I would like for it to store multiple IDs in a JSON file and to have a [p]setchannel command, that would allow me to add one.
I tried this guide, with no luck.
What you probably want to do is store an array of IDs so that you can retrieve them later.
You should have a channelIDs property in your JSON file set to an empty array. Inside your code you can fetch it like this:
const { channelIDs } = require('./config.json') // Now it's an empty array: []
When you want to update this array you should update your local one first, and then you can update the config file: to do that you can use fs.writeFileSync() in combination with JSON.stringify().
const fs = require('fs')
function addChannelID(id) {
channelIDs.push(id) // Push the new ID to the array
let newConfigObj = { // Create the new object...
...require('./config.json'), // ...by taking all the current values...
channelIDs // ...and updating channelIDs
}
// Create the new string for the file so that it's not too difficult to read
let newFileString = JSON.stringify(newConfigObj, null, 2)
fs.writeFileSync('./config.json', newFileString) // Update the file
}
Once you set this function you can add a new ID every time you want, just by calling addChannelID('channel_id').
To check whether the channel the message is coming from should be considered you can use this:
if (channelIDs.includes(message.channel.id)) {
// OK
}
I have a data like this:
"customers": {
"aHh4OTQ2NTlAa2xvYXAuY29t": {
"customerId": "xxx",
"name": "yyy",
"subscription": "zzz"
}
}
I need to retrive a customer by customerId. The parent key is just B64 encoded mail address due to path limitations. Usually I am querying data by this email address, but for a few occasions I know only customerId. I've tried this:
getCustomersRef()
.orderByChild('customerId')
.equalTo(customerId)
.limitToFirst(1)
.once('child_added', cb);
This works nicely in case the customer really exists. In opposite case the callback is never called.
I tried value event which works, but that gives me whole tree starting with encoded email address so I cannot reach the actual data inside. Or can I?
I have found this answer Test if a data exist in Firebase, but that again assumes that you I know all path elements.
getCustomersRef().once('value', (snapshot) => {
snapshot.hasChild(`customerId/${customerId}`);
});
What else I can do here ?
Update
I think I found solution, but it doesn't feel right.
let found = null;
snapshot.forEach((childSnapshot) => {
found = childSnapshot.val();
});
return found;
old; misunderstood the question :
If you know the "endcodedB64Email", this is the way.:
var endcodedB64Email = B64_encoded_mail_address;
firebase.database().ref(`customers/${endcodedB64Email}`).once("value").then(snapshot => {
// this is getting your customerId/uid. Remember to set your rules up for your database for security! Check out tutorials on YouTube/Firebase's channel.
var uid = snapshot.val().customerId;
console.log(uid) // would return 'xxx' from looking at your database
// you want to check with '.hasChild()'? If you type in e.g. 'snapshot.hasChild(`customerId`)' then this would return true, because 'customerId' exists in your database if I am not wrong ...
});
UPDATE (correction) :
We have to know at least one key. So if you under some circumstances
only know the customer-uid-key, then I would do it like this.:
// this is the customer-uid-key that is know.
var uid = firebase.auth().currentUser.uid; // this fetches the user-id, referring to the current user logged in with the firebase-login-function
// this is the "B64EmailKey" that we will find if there is a match in the firebase-database
var B64EmailUserKey = undefined;
// "take a picture" of alle the values under the key "customers" in the Firebase database-JSON-object
firebase.database().ref("customers").once("value").then(snapshot => {
// this counter-variable is used to know when the last key in the "customers"-object is passed
var i = 0;
// run a loop on all values under "customers". "B64EmailKey" is a parameter. This parameter stores data; in this case the value for the current "snapshot"-value getting caught
snapshot.forEach(B64EmailKey => {
// increase the counter by 1 every time a new key is run
i++;
// this variable defines the value (an object in this case)
var B64EmailKey_value = B64EmailKey.val();
// if there is a match for "customerId" under any of the "B64EmailKey"-keys, then we have found the corresponding correct email linked to that uid
if (B64EmailKey_value.customerId === uid) {
// save the "B64EmailKey"-value/key and quit the function
B64EmailUserKey = B64EmailKey_value.customerId;
return B64UserKeyAction(B64EmailUserKey)
}
// if no linked "B64EmailUserKey" was found to the "uid"
if (i === Object.keys(snapshot).length) {
// the last key (B64EmailKey) under "customers" was returned. e.g. no "B64EmailUserKey" linkage to the "uid" was found
return console.log("Could not find an email linked to your account.")
}
});
});
// run your corresponding actions here
function B64UserKeyAction (emailEncrypted) {
return console.log(`The email-key for user: ${auth.currentUser.uid} is ${emailEncrypted}`)
}
I recommend putting this in a function or class, so you can easily call it up and reuse the code in an organized way.
I also want to add that the rules for your firebase must be defined to make everything secure. And if sensitive data must be calculated (e.g. price), then do this on server-side of Firebase! Use Cloud Functions. This is new for Firebase 2017.
I'm still struggling to understand how to access Meteor.users as a foreign key from another collection query. I understand that only the current user is published by default so I have a publication on the server as
Meteor.publish('itemOwner', function(userId) {
check(userId, String);
var user = Meteor.users.find({id: userId});
return user;
// return Meteor.users.find({id: userId}, {
// fields: {'profile': 1} });
});
I then have a Deps.autorun on the client..
Deps.autorun(function () {
var itemOwnerId = Session.get("itemOwnerID");
if (itemOwnerId) {
debugger
var ID = Session.get("itemOwnerID");
Meteor.subscribe('itemOwner', Session.get("itemOwnerID"));
}
});
I set the session ID on a modal form load, and display it in the template by calling the ownerProfile helper (or try to)
Template.showQuoteModalInner.helpers({
getQuote: function () {
// Get the quote ID from the session var
var quote = Session.get("quoteID");
if(quote) {
debugger;
var ID = quote.user._id;
Session.set("itemOwnerID", quote.user._id);
return quote;
}
},
ownerProfile: function() {
debugger;
var quote = Session.get("quoteID");
if(quote) {
var ID = quote.user._id;
var theUser = Meteor.users.find({_id: quote.user._id});
return theUser;
};
}
});
Now, I can trace the user ID at each stage and see it getting correctly passed to the autorun and the helpers. If I stop the program at the debugger in the ownerProfile helper and in the console put in Meteor.user.fetch({_id: "the id here"}).fetch() I get the correct user back.. but, in the handler itself the Meteor.users.find returns null??? What am I missing?
Two possibilities I noticed.
First, you are missing an underscore in the find in your publish function.
.find({id: userId}) should be .find({_id: userId}).
But this probably isn't the issue if you are seeing the user (other than the logged in user) in the console.
Second, if you are not seeing the user from your Template.showQuoteModalInner.ownerProfile helper, it is probably because you are returning a find() instead of a findOne().
find() returns a cursor whereas findOne() returns the record. Try findOne() if you want to display that single user's attributes.