Checking a child's entries for a match - firebase

let ref = firebase.database().ref('players').child(playerId).child('voters');
ref.child(uid).once('value', snap => {
var key = snap.key;
console.log("snapkey: " + key + " uid: " + uid)
if (key === uid) {
console.log("Exists")
} else {
console.log("Doesn't exist")
}
});
I'm trying to see if a variable uid, which holds the users unique ID from firebase-auth is present in my database's voters
So for me, when I'm using the app, my uid is vKl6rIUuI0WsbeWVORz3twPUfnd2. So if I go to vote on this Firstname Lastname person, it should tell me I exist in the above image's scenario.
The problem is, it seems to always say it exists. The console.log for key and uid are both putting out my uid. Is it something with the ref.child(uid)...?

let ref = firebase.database().ref('/players/' + playerID + '/voters');
ref.once('value', snap => {
var value = snap.val()
console.log(value)
if (value !== null) {
console.log("Exists")
} else {
console.log("Doesn't exist")
}
});
https://firebase.google.com/docs/database/web/read-and-write#read_data_once

A snapshot will always have a key. Always. And it will be at the location you requested by reference. Whether or not there is data behind that key is irrelevant to the fact that the snapshot will always have a key.
What you need to do is check the data behind that key. Is it null? Then there's no data there. A number? That's data, and it's present.

Use .exists() method:
let ref = firebase.database().ref('players').child(playerId).child('voters');
ref.child(uid).once('value', (snap) => {
console.log(snap.exists()); // This will print true or false
});

Related

How can I use AQL with multiple queries that using the result of one another?

I have 2 vertices and an edge named user, device, ownership respectively.
My business logic is when I receive device information, I upsert it with dateCreated and dateUpdated fields added. If I inserted that device then I insert new user with default values and create edge connection to it. If I update I simple return already connected user as a result.
Without losing atomicity how can I achieve this?
I tried single AQL query but without condition it is not possible it seems and traversal also is not supported with insert/update operation.
I can do separate queries but that loses atomicity.
var finalQuery = aql`
UPSERT ${deviceQuery}
INSERT MERGE(${deviceQuery},{dateCreated:DATE_NOW()})
UPDATE MERGE(${deviceQuery},{dateUpdated:DATE_NOW()})
IN ${this.DeviceModel}
RETURN { doc: NEW, type: OLD ? 'update' : 'insert' }`;
var cursor = await db.query(finalQuery);
var result = await cursor.next();
if (result.type == 'insert') {
console.log('Inserted documents')
finalQuery = aql`
LET user=(INSERT {
"_key":UUID(),
"name": "User"
} INTO user
RETURN NEW)
INSERT {
_from:${result.doc._id},
_to:user[0]._id,
"type": "belongs"
}INTO ownership
return user[0]`;
cursor = await db.query(finalQuery);
result = await cursor.next();
console.log('New user:',result);
}
You can try something like this
Upsert ....
FILTER !OLD
Let model = NEW
LET user= First(INSERT {
"_key":UUID(),
"name": "User"
} INTO user
RETURN NEW)
INSERT {
_from:model._id,
_to:user._id,
"type": "belongs"
}INTO ownership
return user
I end up separating the modification and selection queries.
var finalQuery = aql`
LET device=(
UPSERT ${deviceQuery}
INSERT MERGE(${deviceQuery},{dateCreated:DATE_NOW()})
UPDATE MERGE(${deviceQuery},{dateUpdated:DATE_NOW()})
IN ${this.DeviceModel}
RETURN { doc: NEW, type: OLD ? 'update' : 'insert' })
FILTER device[0].type=='insert'
LET user=(INSERT {
"_key":UUID(),
"name": "User"
} INTO user
RETURN NEW)
INSERT {
_from:device[0].doc._id,
_to:user[0]._id,
"type": "belongs"
}INTO ownership
return user[0]`;
var cursor = await db.query(finalQuery);
var result = await cursor.next();
if (result == null) {
const deviceId=this.DeviceModel.name+"/"+queryParams._key;
finalQuery = aql`
FOR v,e,p IN 1..1
OUTBOUND ${deviceId} ownership
FILTER e.type=="belongs"
RETURN v `;
cursor = await db.query(finalQuery);
result = await cursor.next();
isUpdate=true;
}
This way I ensure the atomicity. There are improvements for controling if cursor.extra.stats.writesExecuted true etc.

firebase realtime database find node by child value

I have the following structure:
root
-LKMsdf2_Qxbwtv4238D
details
uid: john
-YKMWrmj_QxbwtvSBM5A
details
uid: tony
-R45Wrmj_Qxbf321BMd4
details
uid: karina
How can I find the ref key under 'root' by its uid:
e.g: by uid:karina I need to get the ref key -R45Wrmj_Qxbf321BMd4
is there a way to use some wildcard like /root/{recordid}/details/uid or something?
======== Thanks for the hints! ==== here is my final solution ================
findEntry = function(targetUid) {
var entriesRef = db.ref('root');
return entriesRef.once('value')
.then((snapshot)=>{
var id = []; // found id
snapshot.forEach((childSnapshot)=>{
var childKey = childSnapshot.key;
var childData = childSnapshot.val();
var found = (childData.uid === targetUid);
if (found) {
console.log('Found for uid:' + targetUid + ': ' + childKey);
id = childKey;
}
return found; // true - breaks the forEach, false - continue
});
if (!id) {
console.log('Not Found for uid:' + targetUid);
}
return id;
});
}
No, the best you can do is child (key) search and equality (see the example here https://firebase.google.com/docs/reference/js/firebase.database.Query#equalTo)
// Find all dinosaurs whose height is exactly 25 meters.
var ref = firebase.database().ref("dinosaurs");
ref.orderByChild("height").equalTo(25).on("child_added", function(snapshot) {
console.log(snapshot.key);
});
There isn't a way to query deeper than that.
You could have other structures for reverse lookups, flatten out your data or solve it in a different way.

Firebase Cloud Functions wait for variables

I wanna write img_thumb_url to firebase database. In func1A, my code i wrote below where I expect it can breakthrough the while loop when those global variable room and msg_key are not null but what I found it always undefined although those variables become defined with string value when func2B successfully trigggered.
...
while(1){
if(room!=null && msg_key!=null){
console.log('room is ', room, '. msg_key is ', msg_key);
console.log('break from the loop');
break;
}
}
admin.initializeApp();
return admin.database().ref('/msgs/' + room + '/chat/' + msg_key + '/attachment/photo/thumbnail/url2').set(img_thumb_url);
})
But the path above ' '/msgs/' + room + '/chat/' + msg_key + '/attachment/photo/thumbnail/url2 ' depends on variable room and msg_key retrieved from other function func2B which is here,
exports.func2B = functions.database.ref('/msgs/{roomName}/chat/{pushid}')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const msg = snapshot.val();
if (msg.attachPhotoUrl == null) {
console.log('No photo attachment found');
return null;
}
if (msg_key != null) {
console.log('msg_key no longer null');
return null;
}
console.log('writing url2 for thumbnail in firebase database...');
msg_key = msg.key;
room = msg.roomName;
console.log('room is ',room);
console.log('msg_key is ',msg_key);
console.log('img_thumb_url: ',img_thumb_url);
return ....
});
I'm not sure whether this is the proper way of method but I don't think it is as simple as that. Please help me how to resolve. How can i get that assigned value variable room and msg_key in func2B to func1A?

Add value to Firebase (AngularFire)

I try to add new value to database but I do not know how to add without generating the unique key (Highlighted part)
Here is what I have done so far. Any help would be appreciated!
var newRef = GetArrayFactory('users',firebaseUser.uid); //reference to '5QMI4UsOeAXZUxxkuy5jozcrrSK2'
newRef.$add(
{ 'email': email }
).then(function(newRef){
var id = newRef.key;
console.log("added record with id " + id);
list.$indexFor(id); // returns location in the array
});

Most efficient way to ensure user owns document on update?

I'm using Meteor methods to update documents so I can share them easier and have more control. However i've ran into a problem with checking ownership.
How should I check to make sure the user calling the update method is the owner of the document? Currently i'm grabbing the document first then running the update.
Is there a better pattern to accomplish this?
Meteor.methods({
'Listing.update': function(docId, data) {
var doc = db.listings.findOne({_id: docId}) || {};
if (doc.userId !== this.userId) {
throw new Meteor.Error(504, "You don't own post");
}
// ensure data is the type we expect
check(data, {
title: String,
desc: String
});
return db.listings.update(docId, {$set: data});
}
});
You don't need the additional db call to fetch the original doc, just make the userId an additional criteria in the update selector. If no doc exists with the correct _id and userId no update will be done. update returns the number of docs updated so it will return 1 on success and 0 on failure.
like this:
'Listing.update': function(docId, data) {
var self = this;
check(data, {
title: String,
desc: String
});
if ( ! self.userId )
throw new Meteor.Error(500, 'Must be logged in to update listing');
res = db.listings.update({_id: docId, userId: self.userId}, {$set: data});
if ( res === 0 )
throw new Meteor.Error( 504, "You do not own a post with that id" );
return res;
}
Also, if you use findOne to check a document's existence, use the fields option to limit what you return from the db. Usually just {fields: {_id:1}}.

Resources