Can my FireBase database layout cause issues if I use this approach? - firebase-realtime-database

I have a FireBase structure that looks like this. I want to represent all countries in the world.
In this sample code there is only 9 countries and only United States and Venezuela have data to demonstrate my problem.
I have denormalization and flattens the data as much as I can.
What happens her is that user can search for street addresses like
US/California/Orange County/Orange/3138 E Maple Ave
In the db below it looks like this:
US/ADMINISTRATIVE_AREA_LEVEL_1/
US/ADMINISTRATIVE_AREA_LEVEL_2
US/LOCALITY
US/STREET_ADDRESS
....
....
"AE": {
"name": "United Arab Emirates"
},
"GB": {
"name": "United Kingdom"
},
"US": {
"name": "United States"
"ADMINISTRATIVE_AREA_LEVEL_1": {
"hjg86tghg8hubyhiuhb88ihi": {
"level1": "California"
},
},
"ADMINISTRATIVE_AREA_LEVEL_2": {
"hjg86tghg8hubyhiuhb88ihi": {
"level2": "Orange County"
},
},
"LOCALITY": {
"hjg86tghg8hubyhiuhb88ihi": {
"level2": "Orange"
},
},
"STREET_ADDRESS": {
"hjg86tghg8hubyhiuhb88ihi": {
"3138 E Maple Ave": {
}
}
},
"USER_LIST": {
"hjg86tghg8hubyhiuhb88ihi": {
"name": "Jhon Doe",
}
},
"CHAT_LIST": {
"hjg86tghg8hubyhiuhb88ihi": {
"title": "Wam-Bam-CHAT",
}
},
"chat_members": {
"hjg86tghg8hubyhiuhb88ihi": {
}
},
"chat_messages": {
"hjg86tghg8hubyhiuhb88ihi": {
},
},
"UM": {
"name": "United States Minor Outlying Islands"
},
"UY": {
"name": "Uruguay"
},
"UZ": {
"name": "Uzbekistan"
},
"VU": {
"name": "Vanuate"
},
"VE": {
"name": "Venezuela"
"ADMINISTRATIVE_AREA_LEVEL_1": {
"swdkewsjdr34378943489324": {
"level1": "California"
},
},
"ADMINISTRATIVE_AREA_LEVEL_2": {
"swdkewsjdr34378943489324": {
"level2": "Orange County"
},
},
"LOCALITY": {
"swdkewsjdr34378943489324": {
"level2": "Orange"
},
},
"STREET_ADDRESS": {
"swdkewsjdr34378943489324": {
"3138 E Maple Ave": {
}
}
},
"USER_LIST": {
"swdkewsjdr34378943489324": {
"name": "Jhon Doe",
}
},
"CHAT_LIST": {
"swdkewsjdr34378943489324": {
"title": "Wam-Bam-CHAT",
}
},
"chat_members": {
"swdkewsjdr34378943489324": {
}
},
"chat_messages": {
"swdkewsjdr34378943489324": {
},
},
"VN": {
"name": "Viet Nam"
....
....
When I create the Realtime Database Rules likes this: I have to create 240 root nods because of 240 countries right.
ThereĀ“s a loot of ".read": "$uid === auth.uid" and a loot of duplicate json since all ADMINISTRATIVE_AREA_LEVEL_1 and others looks the same.
If i put the ADMINISTRATIVE_AREA_LEVEL_1 as a root i will maybe have miljons of entries and for the STREET_ADDRESS there is 154 million alone for United States not to speak of the "world". So I group them like this, having the Country as key root node.
Small sample:
{
"rules": {
"SE": {
"ADMINISTRATIVE_AREA_LEVEL_1": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
},
"VE": {
"ADMINISTRATIVE_AREA_LEVEL_1": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
}
My question is how can I furthermore make this more efficient and what bottleneck(s) can I look forward to. Is there a way to have this structure and centrally set a rule without explicitly writing it at all location
UPDATE
Probably i get this wrong but anyway here goes, after #FrankvanPuffelen answer I try this: Must test run this but does this work on all 240 countries in my above code..
{
"rules": {
"$country": {
"ADMINISTRATIVE_AREA_LEVEL_1": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
"ADMINISTRATIVE_AREA_LEVEL_2": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
"LOCALITY": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
....and more
}
}
}
UPDATE
Yes it works with a little tweek
{
"rules": {
"$hubaBuba": {
"ADMINISTRATIVE_AREA_LEVEL_1": {
".read": "auth != null",
".write": "auth != null",
},
"ADMINISTRATIVE_AREA_LEVEL_2": {
".read": "auth != null",
".write": "auth != null",
},
"LOCALITY": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
},
....and more
}
}

If the rules for each country are the same, you can use a wildcard for the country:
{
"rules": {
"$country": {
"ADMINISTRATIVE_AREA_LEVEL_1": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
}

Related

Why does this .indexOn: 'g' no longer work as it should?

I can no longer place activeDrivers in the correct position to make it work. "acriveDrivers" is a direct branch of the root, so the way I placed it is as if it were a child of drivers, which seems wrong to me, but I can't find the solution.
The rules at the beginning were like this (practically no security):
{
"rules": {
".read":true,
".write": true,
"activeDrivers": {
".indexOn": ["g"]
}
}
}
New rules are:
"rules": {
"drivers": {
"$uid": {
".read": "auth !== null && auth.uid === $uid",
".write": "auth !== null && auth.uid === $uid",
"activeDrivers": {
".indexOn": ["g"]
}
}
}
}
This is the JSON of the Database:
{
"ALL Ride Requests": {
"-NJlIOrp49pNBSuuEawT": {
"destination": {
"latitude": "41.929612",
"longitude": "12.4858561"
},
"destinationAddress": "Duke's",
"driverId": "waiting",
"origin": {
"latitude": "41.919225",
"longitude": "12.5266267"
},
"originAddress": "Circonvallazione Nomentana, 270, 00162 Roma RM, Italy",
"time": "2022-12-20 21:50:48.437521",
"userName": "Peter Parker",
"userPhone": "090012321354"
},
},
"activeDrivers": {
"31uMBJKvl6PF7BVKQnqHORTrWRP2": {
".priority": "sr2yt23wk8",
"g": "sr2yt23wl8",
"l": [
41.926275,
12.5376567
]
},
"G3OJ79KiLeMmxqoUpJ1NggIKbiF2": {
".priority": "sr2yt23wk8",
"g": "sr2yt23rk8",
"l": [
41.926275,
12.5376567
]
},
"filTJu3xjiQYTzehD71kzY1Oybz1": {
".priority": "sr2yt23wk8",
"g": "sr2yy23wk8",
"l": [
41.926275,
12.5376567
]
}
},
"drivers": {
"G3OJ79KiLeMmxqoUpJ1NggIKbiF2": {
"earnings": "0.0",
"email": "simo#hotmail.it",
"id": "G3OJ79KiLeMmxqoUpJ1NggIKbiF2",
"name": "Simone Demichele Test",
"newRideStatus": "idle",
"password": "testpassword",
"phone": "3452362585",
"token": "fFIwv1IHRv2ylXyW-qRVGQ:APA91bH7IfcNBBi7Y53wRjQKN12-nBUgFHHpf7F0LeWCstG_MIqt-mkobRN6nvUZxqbMnMlXU2yMHdE-efYykUdtcXl-91wW5rGyQcpMl6Dij6bxC8snQRkAMGBhQUmyqYW6sBhY6Ul3"
},
"Gzs0qxNiE6VZrwNGPYpFPRB7J8p1": {
"email": "giacomo#gmail.com",
"id": "Gzs0qxNiE6VZrwNGPYpFPRB7J8p1",
"name": "giacomo",
"newRideStatus": "idle",
"phone": "134634564352",
"token": "cdG8W0lFRZ2F0jAeG2WbnO:APA91bHTtbtSg18sPNxcH92TIdM3UuX-qcim2-h0I_1YPcBgucIGO0I3ACIQEGVpmsg-EKcRZXFIMIXlxGk1iBw-V0hmDZBVWDyFh44yH2iM8fZgrhRsQzyVBdXdLxGMI0Vz67hQFUQU"
},
"OLhsoowb9FXTBtWQmGysGLLBgNz2": {
"email": "gino#gmail.com",
"id": "OLhsoowb9FXTBtWQmGysGLLBgNz2",
"name": "Gino",
"phone": "4568965432",
"token": "ewktnxHKRSCBWwilm_SDfd:APA91bFchfCs_33zZNGNtbUHPf6TBC3Vrua7U4QT0bX_e4cr6Z62XSPr5TwZyL-BQ12fmDa3XKnAqeLDQy7NMUKp9m8bq676jS_i5n1vjNMFYJ4tHfIYjntEwYKvOcsTp0yh4ZSvjuxb"
},
"ZYqM7SbG3IhRBAGCfXRZHHs7XdG3": {
"email": "laura#gmail.com",
"id": "ZYqM7SbG3IhRBAGCfXRZHHs7XdG3",
"name": "Laura",
"newRideStatus": "idle",
"phone": "1212432554325",
"token": "cdG8W0lFRZ2F0jAeG2WbnO:APA91bHTtbtSg18sPNxcH92TIdM3UuX-qcim2-h0I_1YPcBgucIGO0I3ACIQEGVpmsg-EKcRZXFIMIXlxGk1iBw-V0hmDZBVWDyFh44yH2iM8fZgrhRsQzyVBdXdLxGMI0Vz67hQFUQU"
},
"filTJu3xjiQYTzehD71kzY1Oybz1": {
"email": "maria#gmail.com",
"id": "filTJu3xjiQYTzehD71kzY1Oybz1",
"name": "Maria",
"newRideStatus": "idle",
"phone": "3726761919",
"token": "fFIwv1IHRv2ylXyW-qRVGQ:APA91bH7IfcNBBi7Y53wRjQKN12-nBUgFHHpf7F0LeWCstG_MIqt-mkobRN6nvUZxqbMnMlXU2yMHdE-efYykUdtcXl-91wW5rGyQcpMl6Dij6bxC8snQRkAMGBhQUmyqYW6sBhY6Ul3"
}
},
"users": {
"yHc7xseCT0QePnpolGHZvdr8ARV2": {
"email": "peter#gmail.com",
"id": "yHc7xseCT0QePnpolGHZvdr8ARV2",
"name": "Peter Parker",
"phone": "090012321354"
}
}
}
This is the query
Position pos = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
driverCurrentPosition = pos;
Geofire.initialize("activeDrivers");
Geofire.setLocation(
currentFirebaseUser!.uid,
driverCurrentPosition!.latitude,
driverCurrentPosition!.longitude
);

Syntax error in database rules while deploying

While deploying my website to firebase,i get a syntax error in database rules showing:
2:5: Expected rules property.
I am listing down the code in json
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
},
"database": {
"rules": "firebase.json"
},
"emulators": {
"auth": {
"port": 9099
},
"database": {
"port": 9000
},
"hosting": {
"port": 5000
},
"ui": {
"enabled": true
}
}
}
The rules value should point to a rules file, not at the firebase.json file itself. Something like:
"rules": "database.rules.json"
That rules file must define valid database rules. For example:
{
"rules": {
".read": false,
".write": false
}
}

Firebase Realtime Database rules for specific property

I use Firebase Realtime Database in my project. JSON structure is:
"records" : {
"FwdxA8vqUBW79nT9o9447RT4Yqa2" : {
"-Lzqsastr4Ywnljvsn-b" : {
"creationDate" : 1.580395032274364E9,
"filename" : "4822DF69-8170-4BEE-92CC-18D3435A4E52",
"length" : "00:07",
"likeCount" : 0,
"link" : "",
"playingCount" : 15,
"recordId" : "-Lzqsastr4Ywnljvsn-b",
"recordUrl" : "https://firebasestorage.googleapis.com.....,
"source" : "",
"title" : "Ftbf",
"userUid" : "FwdxA8vqUBW79nT9o9447RT4Yqa2"
},
"FwdxA8vqUBW79nT9o9447RT4Yqa2" - it's userUid
"-Lzqsastr4Ywnljvsn-b" - it's recordId
How to set security rules just for "likeCount" and "playingCount":
".write": "auth != null",
and for the whole "records" :
"$uid" : {
".write": "auth != null && auth.uid == $uid",
}
Update: I did it)). So, the correct rules for me are:
{
"rules": {
".read": "auth != null",
"records": {
".read": "auth != null",
"$uid": {
".write": "auth != null && auth.uid == $uid",
"$recordId": {
"playingCount": {
".write": "auth != null",
},
"likeCount": {
".write": "auth != null",
},
}
}
},
}
}

Display series labels at the end of each line in Highcharts Editor

I am a beginner in Highcharts and I am trying to achieve what has been explained in the following case using the Highcharts Editor:
Highcharts Line Chart, display series name at the end of line series
I don't know if the same can be achieved by just using the Editor's interface but if custom code needs to be used, I can do that in the Custom Code tab of the Editor.
I tried inputting in the Custom Code section of the Highcharts Editor the code written by jlbriggs but nothing happens.
Is it possible to get a basic explanation on how to achieve what I want?
Below is the HTML code I took from the graph I created in Highcharts Editor.
<div id="highcharts-825789cd-edd9-4d9e-aba4-e3a80fa42369"></div>
<script>
(function() {
var files = ["https://code.highcharts.com/stock/highstock.js", "https://code.highcharts.com/highcharts-more.js", "https://code.highcharts.com/highcharts-3d.js", "https://code.highcharts.com/modules/data.js", "https://code.highcharts.com/modules/exporting.js", "https://code.highcharts.com/modules/funnel.js", "https://code.highcharts.com/modules/annotations.js", "https://code.highcharts.com/modules/solid-gauge.js"],
loaded = 0;
if (typeof window["HighchartsEditor"] === "undefined") {
window.HighchartsEditor = {
ondone: [cl],
hasWrapped: false,
hasLoaded: false
};
include(files[0]);
} else {
if (window.HighchartsEditor.hasLoaded) {
cl();
} else {
window.HighchartsEditor.ondone.push(cl);
}
}
function isScriptAlreadyIncluded(src) {
var scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].hasAttribute("src")) {
if ((scripts[i].getAttribute("src") || "").indexOf(src) >= 0 || (scripts[i].getAttribute("src") === "http://code.highcharts.com/highcharts.js" && src === "https://code.highcharts.com/stock/highstock.js")) {
return true;
}
}
}
return false;
}
function check() {
if (loaded === files.length) {
for (var i = 0; i < window.HighchartsEditor.ondone.length; i++) {
try {
window.HighchartsEditor.ondone[i]();
} catch (e) {
console.error(e);
}
}
window.HighchartsEditor.hasLoaded = true;
}
}
function include(script) {
function next() {
++loaded;
if (loaded < files.length) {
include(files[loaded]);
}
check();
}
if (isScriptAlreadyIncluded(script)) {
return next();
}
var sc = document.createElement("script");
sc.src = script;
sc.type = "text/javascript";
sc.onload = function() {
next();
};
document.head.appendChild(sc);
}
function each(a, fn) {
if (typeof a.forEach !== "undefined") {
a.forEach(fn);
} else {
for (var i = 0; i < a.length; i++) {
if (fn) {
fn(a[i]);
}
}
}
}
var inc = {},
incl = [];
each(document.querySelectorAll("script"), function(t) {
inc[t.src.substr(0, t.src.indexOf("?"))] = 1;
});
function cl() {
if (typeof window["Highcharts"] !== "undefined") {
var options = {
"title": {
"text": "One year, five year and 10 year survival estimates for the 21 most common cancers in England, 1971"
},
"subtitle": {
"text": "Survival estimates in 1971 (%)"
},
"exporting": {},
"chart": {
"type": "line",
"parallelAxes": {
"labels": {
"enabled": true
},
"alignTicks": true,
"endOnTick": true,
"max": 4,
"maxRange": 4,
"minorGridLineWidth": 0,
"gridLineWidth": -1
},
"inverted": false,
"height": 872,
"alignTicks": true,
"showAxes": false,
"ignoreHiddenSeries": true
},
"series": [{
"name": "Oesophagus",
"turboThreshold": 0,
"marker": {
"enabled": true
},
"type": "line",
"colorByPoint": false,
"selected": true,
"showInNavigator": true,
"dataLabels": {
"enabled": false
},
"label": {
"onArea": true,
"enabled": true
},
"allowPointSelect": true,
"xAxis": 0,
"yAxis": 0
}, {
"name": "Stomach",
"turboThreshold": 0
}, {
"name": "Colon",
"turboThreshold": 0
}, {
"name": "Rectum",
"turboThreshold": 0
}, {
"name": "Pancreas",
"turboThreshold": 0,
"marker": {
"enabled": true
},
"type": "line"
}, {
"name": "Larynx",
"turboThreshold": 0
}, {
"name": "Lung",
"turboThreshold": 0
}, {
"name": "Melanoma of skin",
"turboThreshold": 0
}, {
"name": "Breast",
"turboThreshold": 0
}, {
"name": "Cervix",
"turboThreshold": 0
}, {
"name": "Uterus",
"turboThreshold": 0
}, {
"name": "Ovary",
"turboThreshold": 0
}, {
"name": "Prostate",
"turboThreshold": 0,
"marker": {
"enabled": true
},
"colorByPoint": false
}, {
"name": "Testis",
"turboThreshold": 0
}, {
"name": "Kidney",
"turboThreshold": 0
}, {
"name": "Bladder",
"turboThreshold": 0
}, {
"name": "Brain",
"turboThreshold": 0
}, {
"name": "Hodgkin's disease",
"turboThreshold": 0
}, {
"name": "Non-Hodgkin lymphoma",
"turboThreshold": 0
}, {
"name": "Multiple myeloma",
"turboThreshold": 0
}, {
"name": "Leukaemia",
"turboThreshold": 0
}, {
"name": "Other cancers",
"turboThreshold": 0,
"label": {
"connectorAllowed": true
}
}],
"plotOptions": {
"series": {
"dataLabels": {
"enabled": false
}
}
},
"data": {
"csv": "\"Year\";\"Oesophagus\";\"Stomach\";\"Colon\";\"Rectum\";\"Pancreas\";\"Larynx\";\"Lung\";\"Melanoma of skin\";\"Breast\";\"Cervix\";\"Uterus\";\"Ovary\";\"Prostate\";\"Testis\";\"Kidney\";\"Bladder\";\"Brain\";\"Hodgkin's disease\";\"Non-Hodgkin lymphoma\";\"Multiple myeloma\";\"Leukaemia\";\"Other cancers\"\n\"1-year\";14.7;15.3;42.6;54.1;10.2;16;16.3;74.5;74;75.6;43.7;66.1;83.3;44.9;45.4;62.8;17.6;73.9;49.4;36.8;35.4;57.3\n\"5-year\";4;5.2;25.3;23.6;2.4;4.6;4.8;40.5;51.3;59;20.5;36.9;70.5;28.5;28.9;40.9;6.6;54.2;29.3;12.1;13.1;40.4\n\"10-year\";3.3;4;23;19.1;1.3;3.1;3.2;34.9;46;55.5;17.9;25.1;69.2;23;23;33.7;5;45.2;21.7;6.8;6.6;36.9",
"googleSpreadsheetKey": false,
"googleSpreadsheetWorksheet": false
},
"legend": {
"floating": false,
"enabled": false,
"verticalAlign": "bottom",
"align": "center",
"layout": "horizontal"
},
"pane": {
"background": []
},
"responsive": {
"rules": []
},
"colorAxis": {
"plotLines": [{
"label": {
"useHTML": false,
"text": "'series'",
"x": 5,
"y": 5,
"verticalAlign": "'middle'",
"textAlign": "'left'"
}
}],
"plotBands": [{}],
"labels": {
"x": 3,
"y": 2
}
},
"tooltip": {
"shared": true,
"enabled": true
},
"rangeSelector": {
"enabled": false,
"floating": false
},
"credits": {
"enabled": false
},
"xAxis": [{
"type": "category",
"labels": {
"x": 0,
"zIndex": 7
},
"opposite": true
}],
"yAxis": [{
"title": {
"text": ""
},
"labels": {
"format": "{hide}"
},
"type": "linear"
}],
"accessibility": {
"describeSingleSeries": false,
"enabled": true
}
};
new Highcharts.Chart("highcharts-825789cd-edd9-4d9e-aba4-e3a80fa42369", options);
}
}
})();
</script>
I just need the names of each series to be displayed at the end of each line rather than in the legend.
In the "Custom code" section you need to merge your custom options with the options from the editor. There should be an example of how to do that. In essense:
Highcharts.merge(true, options, {
// custom code
});
For example, you could use Highcharts.merge in the following way to achieve this:
Highcharts.merge(true, options, {
plotOptions: {
series: {
dataLabels: {
enabled: true,
formatter: function () {
// if last point
if(this.point === this.series.data[this.series.data.length-1]) {
return this.series.name;
}
}
}
}
}
});
See this Highcharts Cloud example of it in use.

First letter match in elasticsearch aggregations

I'm migrating my elasticsearch from using facets to using aggregations, and I want to create a query where the aggregations represent all the creator names that begin with a certain letter.
I've created a nested index like so:
indexes creators, type: 'nested' do
indexes :name, type: 'string', analyzer: 'caseinsensitive', index: 'not_analyzed'
end
The following query will return all the items where a creator's name begins with a "b". Great working so far.
{
"query": {
"filtered": {
"query": {"match_all": {}},
"filter": {
"nested": {
"path": "creators",
"query": {
"prefix": {
"creators.name": {
"value": "b"
}
}
}
}
}
}
},
"aggregations": {
"creators": {
"nested": {
"path": "creators"
},
"aggs": {
"name": {
"terms": {
"field": "creators.name",
"size": 100
}
}
}
}
}
}
However, the aggregations part of the query returns ALL of the aggregations for the results, including instances creator names that do not begin with a "b." For instance, if I had an item with two creators:
"creators": [
{
"name": "Beyonce"
},
{
"name": "JayZ"
}
],
The aggregation results would include both JayZ and Beyonce. Like most people, I only want Beyonce.
Try this query and see how it goes:
{
"query": {
"filtered": {
"query": {
"match_all": {}
},
"filter": {
"nested": {
"path": "creators",
"query": {
"prefix": {
"creators.name": {
"value": "b"
}
}
}
}
}
}
},
"aggregations": {
"creators": {
"nested": {
"path": "creators"
},
"aggs": {
"NAME": {
"filter": {
"prefix": {
"creators.name": "b"
}
},
"aggs": {
"name": {
"terms": {
"field": "creators.name",
"size": 100
}
}
}
}
}
}
}
}

Resources