I'm currently working with the node library firebase and I want to search for a single value by using database refs.
I've been able to search for every single record on the realtime-database by returning the ref itself from my CharacterService's getAll method, but I can't seem to find a way to do it for jus a single record.
Firebase (Configuration file)
import firebase from 'firebase';
import config from '../config';
if (firebase.apps.length === 0) firebase.initializeApp(config.firebase);
const database = firebase.database();
export default database;
CharacterService
import firebase from 'config/firebase';
const db = firebase.ref('/character');
class CharacterService {
getAll () {
return db;
}
create (character) {
return db.push(character);
}
update (key, value) {
return db.child(key).update(value);
}
delete (key) {
return db.child(key).remove();
}
deleteAll () {
return db.remove();
}
}
export default new CharacterService();
Database records
{
"character" : {
"-MLEm-8ehLQ4repmeKtl" : {
"age" : 36,
"exp" : {
"current" : 0,
"toLvlUp" : 125,
"total" : 0
},
"gender" : "Male",
"level" : 1,
"name" : "Aleksandr Brightwood"
},
"-MLEm-7fhLQ5rehltKjk" : {
"age" : 22,
"exp" : {
"current" : 0,
"toLvlUp" : 125,
"total" : 0
},
"gender" : "Female",
"level" : 1,
"name" : "Sarah Autumn"
}
}
}
You can certainly get a child node using its key.
const childRef = firebase.ref('/character').child(key);
Now you have a Reference you can use to get only the data under that nested child.
I suggest reviewing the documentation for more information.
Related
This is 'product.js' file, and when ever I am calling the api '/achaar/products/1', the value of val is giving empty in console. Other api calls like '/achaar/products' is working fine, but on calling with id this is not working.
const express = require('express')
const router = express.Router();
const json_data = require('./test_data');
const cons = require('./constants')
/*
url is : /achaar/products
replace test data with database datas
*/
router.get(cons.URLS.all_product,(req,res) => {
res.json(json_data)
})
router.get(cons.URLS.all_product+'/:id',(req,res) => {
console.log(req.params.id)
var val = json_data.achaar.filter(function (x) { return x.id === req.params.id })
console.log(val)
})
module.exports = router
Here is my test_data.js file
{
"achaar" : [
{
"id" : 0,
"name" : "Ginger Tangi",
"cost" : 2000
},
{
"id" : 1,
"name" : "Mango Khatta",
"cost" : 1200
},
{
"id" : 2,
"name" : "Gaas Dhari",
"cost" : 3000
},
{
"id" : 3,
"name" : "Cream Tangy",
"cost" : 1000
}
]
}
Because you use === and it was check value and type is equal
You should convert req.params.id to number because id of data is number.
I'm writing cloud functions for my firebase realtime database (not firestore) using js sdk. These functions increment counters that count the number of users per area. The areas I'm using are geographical and nested (Continent>Country>Region>City). The functions are triggered by the insertion of a user into the users list of a given area (.onCreate). If a user is inserted into the users list of let's say a country, it will automatically be added to the users list of the parent continent.
After tinkering for some time I was only able to come up with a "low-level" non-scalable solution (which by the way works). This means that I need to write additionnal code if I want to add a child area to city for example.
This is a problem because I expect to add child areas in the future.
Question: Is there a way to write a generic cloud function that increments the counters of a node and the counters of all its parent nodes?
I'm quite new to firebase and any help would be really appreciated
Json structure of firebase realtime db
{
"groups" : {
"location" : {
"continents" : {
"EU" : {
"countries" : {
"DE" : {
"created" : "2019-03-25--20:03:34",
"flag" : "/flags/de.svg",
"label" : "Germany",
"level" : 2,
"regions" : {
"BE" : {
"cities" : {
"Berlin" : {
"created" : "2019-03-25--20:03:35",
"label" : "Berlin",
"level" : 3,
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:55",
"label" : "Anonymous"
}
}
}
},
"created" : "2019-03-25--20:03:35",
"label" : "Land Berlin",
"level" : 3,
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:52",
"label" : "Anonymous"
}
}
}
},
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:49",
"label" : "Anonymous"
}
}
}
},
"created" : "2019-03-25--20:03:33",
"label" : "Europe",
"level" : 1,
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:46",
"label" : "Anonymous"
}
}
}
}
}
}
}
My cloud functions for user insertion - still need to write for user deletion
exports.onUserAddToContinent = functions.database
.ref('/groups/location/continents/{continentID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
exports.onUserAddToCountry = functions.database
.ref('/groups/location/continents/{continentID}/countries/{countryID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const countryId = context.params.countryID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/countries/' + countryId + '/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
exports.onUserAddToRegion = functions.database
.ref('/groups/location/continents/{continentID}/countries/{countryID}/regions/{regionID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const countryId = context.params.countryID
const regionId = context.params.regionID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/countries/' + countryId + '/regions/' + regionId +'/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
exports.onUserAddToCity = functions.database
.ref('/groups/location/continents/{continentID}/countries/{countryID}/regions/{regionID}/cities/{cityID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const countryId = context.params.countryID
const regionId = context.params.regionID
const cityId = context.params.cityID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/countries/' + countryId + '/regions/' + regionId +'/cities/'+ cityId + '/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
I feel 'im missing something. There has to be a generic way of doing this.
Before version (<= v0.9.1) it was possible to set data like that and now not anymore...
event.data.ref.child('thisname').set("error");
edit: setter are not mentioned here! Only how to receive values from onUpdate or onWrite
https://firebase.google.com/docs/functions/beta-v1-diff
Solution is posted below
Firebase: (supports version 1.0 or 2.0)
If someone else has problems changing / setting / updating values in his cloud functions inside an onUpdate or onWrite trigger, then this might help u...
First this is how my data tree looks like:
"users" : {
"4h23u45h23509hu346034h5943h5" : {
"address" : "Backouse 2",
"city" : "Los Angeles",
"name" : "Joseph",
...
},
"23u4g24hg234h2ui342b34hi243n" : {
"address" : "Streetouse 13",
"city" : "Los Angeles",
"name" : "Stefan",
...
Now to the cloud functions:
Before (<= v0.9.1)
exports.updatingUser = functions.database.ref('/users/{pushId}')
.onUpdate(event => {
var address = event.data.child('address');
var city = event.data.child('city');
if (address.changed() || city.changed()) {
//generateThisname()
if (thisname == null) {
event.data.ref.child('name').set("error"); //Important Part
}
else {
event.data.ref.child('name').set(thisname); //Important Part
}
...
Now (>= v1.0.0)
exports.updatingUser = functions.database.ref('/users/{pushId}')
.onUpdate((change, context) => {
var addressBefore = change.before.child('address').val();
var addressAfter = change.after.child('address').val();
var cityBefore = change.before.child('city').val();
var cityAfter = change.after.child('city').val();
//create reference from root to users/{pushId}/
var rootSnapshot = change.after.ref.parent.child(context.params.pushId)
if ((addressBefore !== addressAfter) || (cityBefore !== cityAfter)) {
//generateThisname()
if (thisname === null) {
rootSnapshot.child('name').set("error");
}
else {
rootSnapshot.child('name').set(thisname);
}
...
So before you can set a value, you first have to make a reference from the root of your database and then go all the way down to you value and call set()
I'm trying to lazy load firebase items to later on load more of them whenever user reaches end of div container. When i remove .endAt() and .startAt() i'm receiving the 15 items though they are not beeing incremented and it's stuck at these 15 items.
When i keep .endAt() and .startAt() i'm receiving firebase warning
Using an unspecified index. Consider adding ".indexOn": "title" at /items even though .indexOn is set. I'm confused by that warning. Thanks in advance for any help.
Firebase structure
{
"items" : {
"-Kk6aHXIyR15XiYh65Ht" : {
"author" : "joe",
"title" : "Product 1"
},
"-Kk6aMQlh6_E3CJt_Pnq" : {
"author" : "joe",
"title" : "Product 2"
}
},
"users" : {
"RG9JSm8cUndpjMfZiN6c657DMIt2" : {
"items" : {
"-Kk6aHZs5xyOWM2fHiPV" : "-Kk6aHXIyR15XiYh65Ht",
"-Kk6aMTJiLSF-RB3CZ-2" : "-Kk6aMQkw5bLQst81ft7"
},
"uid" : "RG9JSm8cUndpjMfZiN6c657DMIt2",
"username" : "joe"
}
}
}
Security rules
{
"rules": {
".read": true,
".write": "auth != null",
"users":{
"$uid": {
".write": "$uid === auth.uid"
"items":{
".indexOn": "title",
"$itemId": {
"title": {".validate": "...}
"type": {".validate": "...}
}
}
}
}
}
}
}
Code structure for lazy load
let _start = 0,
_end = 14,
_n = 15;
function lazyLoadItems(){
firebase.database().ref('items')
.orderByChild('title')
.startAt(_start)
.endAt(_end)
.limitToFirst(_n)
.on("child_added", snapshot=> console.log(snapshot.val()));
_start += _n;
_end += _n;
}
You're misunderstanding how Firebase queries work. It's easiest to see if you use hard-coded values:
firebase.database().ref('items')
.orderByChild('title')
.startAt(0)
.endAt(14)
.limitToFirst(15)
There is no item with title=0 or title=14, so the query doesn't match anything.
Firebase Database queries match on the value of the property you order on. So when you order by title the values you specify in startAt and endAt must be titles. E.g.
ref.child('items')
.orderByChild('title')
.startAt("Product 1")
.endAt("Product 1")
.limitToFirst(15)
.on("child_added", function(snapshot) { console.log(snapshot.val()); });
See for the working sample of this: http://jsbin.com/hamezu/edit?js,console
To implement pagination, you'll have to remember the last item of the previous page and pass that in to the next call: startAt(titleOfLastItemOnPreviousPage, keyOfLastItemOnPreviousPage).
startAfter is the last element
each time you have to send the last element:
const lastAlert = querySnapshot.docs[querySnapshot.docs.length - 1]
here an example:
export const fetchAlerts = (createdForId, startAfter) => {
return new Promise((resolve, reject) => {
firebase
let query = firebase
.firestore()
.collection('alerts')
.where('generated_for.uid', '==', createdForId)
.where('read', '==', false)
.orderBy('created_at', 'desc')
.limit(25)
if (startAfter) {
query = query.startAfter(startAfter)
}
query
.get()
.then(querySnapshot => {
const lastAlert = querySnapshot.docs[querySnapshot.docs.length - 1]
const alerts = querySnapshot.docs.map(doc => ({
...doc.data(),
uid: doc.id,
}))
resolve(alerts)
resolve({ alerts, lastAlert })
})
.catch(error => {
console.log(error.message)
I am trying to add a simple transaction to a firebase database, however, I'm not having luck getting around the firebase generated keys for each account. here is my JSON:
{
"accounts" : {
"-KRPSyO4B48IpBnHBTYg" : {
"newRecord" : "100",
"email" : "",
"provider" : "",
"userId" : ""
}
},
"products" : {
"-KUKRafaNurpFhGF4jQa" : {
"name" : ""
}
},
}
I want to add a count to "newRecord" to the same level as userId, provider and email. However I am not successful with the following, or any variation so far:
firebase.database().ref('accounts/' + accounts.id).transaction(function(value) {
if (value) {
value.newRecord++;
}
return value;
});
Thanks in advance!
You should refer the value directly while using the transaction.
firebase.database().ref('accounts/' + accounts.id+'/newRecord').transaction(function(value) {
if (value) {
value++;
}
return value;
});