mobizt / Firebase-ESP32

[DEPRECATED]šŸ”„ Firebase RTDB Arduino Library for ESP32. The complete, fast, secured and reliable Firebase Arduino client library that supports CRUD (create, read, update, delete) and Stream operations.
MIT License
415 stars 118 forks source link

HELP: Not sure why the two level deep data is not accessed. #22

Closed usrdes closed 5 years ago

usrdes commented 5 years ago

Hi,

Thank you for your quick comments last time. Also thank you for the excellent library.

I was able to get the first level firebase access going with your kind comments last time. I hope you can help me for this as well.

The problem I am having is using query with a second level data in the realtime database.

I took the Basic.ino and updated it as before. This time I inserted a second level data when the code runs and accesses the firebase the first time. Then in the loop, I try to access the two level deep data : /post/post2.

`

================================ Basic.ino modified Code:
/*
 * Created by K. Suwatchai (Mobizt)
 * 
 * Email: k_suwatchai@hotmail.com
 * 
 * Github: https://github.com/mobizt
 * 
 * Copyright (c) 2019 mobizt
 *
*/
//This example shows how to read, store and update database using get, set, push and update functions.
//Required HTTPClientESP32Ex library to be installed  https://github.com/mobizt/HTTPClientESP32Ex

#include <WiFi.h>
#include "FirebaseESP32.h"
#include "mycredentials.h"
#include <Preferences.h>
#include <ArduinoJson.h>

#define FIREBASE_HOST MYHOST //Do not include https:// in FIREBASE_HOST
#define FIREBASE_AUTH MYDBSECRET
#define WIFI_SSID mySSID
#define WIFI_PASSWORD myPWD

FirebaseData firebaseData;

static int av1 = 0; // add some values into strings
static int av2 = 1;

void pushmyData() {
String k1 = "{\"data1\":\"value";
String k2 = "\", \"data2\":{\"child2\":\"_value";
String ke = "\"}}";
//String updateData = "{\"data1\":\"value\", \"data2\":{\"child2\":\"_value\"}}";

String updateData = k1+String(av1)+k2+String(av2)+ke;
   av1++;
   av2++;  
   String s= "Kaboom"+String(av2);
   String s_json = "{\"NewData\":\"Kaboom"+String(av2)+"\"}";
   Serial.println(" My s_json string is : "+s_json);
   String SecondLevel = "{\"post\":{\"post1\":{\"author\":\"TOM\",\"vegitable\":\"tomato\"},\"post2\":{\"author\":\"JERRY\",\"vegitable\":\"potato\"}}}";
   if(av1 == 1 ) { 
      if( Firebase.setString(firebaseData, "/NewData", s) ) {
          Serial.println("Done setString /NewData:"+s);  // should post "Kaboom2" to node "NewData"
      } else {
          Serial.println(firebaseData.errorReason());
      }
      if( Firebase.updateNode(firebaseData, "/", SecondLevel) ) {
          Serial.println("Done second level setString /:"+SecondLevel);  // should pust "Kaboom2" to node "NewData"
      } else {
          Serial.println(firebaseData.errorReason());
      }
  } else {
      if (Firebase.updateNode(firebaseData, "/", s_json)) { // should add "Kaboom3" to "Kaboom2"
          Serial.println("Done updateNode "+s_json);     // Check firebase data base, to see if 
      } else {                                               // The value of NewData is "Kaboom2Kaboom3"
          Serial.println(firebaseData.errorReason());      // does not seem to be working          
      }
  }

if (Firebase.updateNode(firebaseData, "/", updateData)) {

  //Success, then try to read the payload value

  //Database path that updated
  Serial.println(firebaseData.dataPath());

  //Data type at updated database path
  Serial.println(firebaseData.dataType()); //Should be "json" -- yes it is

  //Print the JSON string payload that returned from server
  Serial.println(firebaseData.jsonData()); //OK, this works

  //Actual sent payload JSON data
  Serial.println(updateData);

} else {
  //Failed, then print out the error detail
  Serial.println(firebaseData.errorReason());
}

}

// call this routine in loop to see values that are read back 
void query_firebase () {
QueryFilter query;
query.orderBy("data1"); // try to get only data1

if (Firebase.getJSON(firebaseData, "/", query)) // gets the complete data base at "/"
{
  //Success, then try to read the JSON payload value 
  Serial.println(firebaseData.jsonData());   // prints everything, even if there is a ".indexOn": "data1" on-line at firebase
}
else
{
  //Failed to get JSON data at defined database path, print out the error reason
  Serial.println(firebaseData.errorReason());
}
//Clear all query parameters
query.clear();

}

// call GetSpecificData() in the main loop, and see if you can get specific
// data for node data2, child _data2, with the value

void GetSpecificData() {
  QueryFilter query;
  query.orderBy("child2");;
  String k="No News";
    if(Firebase.getString(firebaseData,"/NewData", k) ) {
      Serial.println("Got NewData as: "+k);
    } else {
      Serial.println(firebaseData.errorReason());
    }
    if (Firebase.getJSON(firebaseData, "/data2", query)) {
    //Success, then read the payload value
    //Make sure payload value returned from server is integer
    //This prevent you to get garbage data

    StaticJsonBuffer<200> jsonBuffer;
    JsonObject& mData = jsonBuffer.parseObject(firebaseData.jsonData());
    mData.printTo(Serial);
    Serial.println("");
    const char* val = mData["child2"];
      Serial.print("Value extracted from json key value pair: ");
      Serial.println(val);
  } else {
    //Failed, then print out the error detail
    Serial.println(firebaseData.errorReason());
  }
  //Clear all query parameters
 query.clear();
}

void GetSpecificSecondLevelData() {
  QueryFilter query;
  query.orderBy("post2");
    if (Firebase.getJSON(firebaseData, "/post", query)) {
    StaticJsonBuffer<200> jsonBuffer;
    JsonObject& mData = jsonBuffer.parseObject(firebaseData.jsonData());
    mData.printTo(Serial);
    Serial.println("");
    const char* val = mData["vegitable"];
      Serial.print("Posted Value extracted from vegitable: ");
      Serial.println(val);
  } else {
    //Failed, then print out the error detail
    Serial.println(firebaseData.errorReason());
  }
  //Clear all query parameters
 query.clear();
}

//////
void setup()
{

  Serial.begin(115200);
  Serial.println();
  Serial.println();
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(300);
  }
  Serial.println();
  Serial.print("Connected with IP: ");
  Serial.println(WiFi.localIP());
  Serial.println();

  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
  Firebase.reconnectWiFi(true);
  Firebase.setMaxRetry(firebaseData, 3);
  Firebase.setMaxErrorQueue(firebaseData, 30);
}

void loop()
{
#define SomeDelay 10000
  Serial.println("======================== Pushing data ================================");
  pushmyData();
  delay(SomeDelay);
  Serial.println("======================== Get specific data ================================");
  GetSpecificData();
  delay(SomeDelay);
  Serial.println("======================== query specific second level data ================================");
  GetSpecificSecondLevelData(); //query_firebase ();
  delay(SomeDelay);
}

=================  Firebase realtime db
{
  "NewData" : "Kaboom4",
  "data1" : "value2",
  "data2" : {
    "child2" : "_value3"
  },
  "post" : {
    "post1" : {
      "author" : "TOM",
      "vegitable" : "tomato"
    },
    "post2" : {
      "author" : "JERRY",
      "vegitable" : "potato"
    }
  }
}
====================== rules
{
    "rules": {
    ".indexOn": ["post", 
                 "post/post1",
                 "post/post2",
                 "post/post1/author",
                 "post/post1/vegitable",
                 "post1/author",
                 "post1/vegitable", 
                 "post/post2/author",
                 "post/post2/vegitable",
                 "post2/author",
                 "post2/vegitable", 
                 "NewData", 
                 "data1",
                 "data2/child2",
                 "child2"], 
     "post":{
       ".read": true,
         ".write": true,
             ".indexOn": ["post1","post2"],
               "post1":{
                 ".indexOn":["author", "vegitable"],
                   "author": {
                   ".read": true,
                     ".write": true
                    },
                   "vegitable": {
                   ".read": true,
                     ".write": true
                    }
                   },
               "post2": {
                 ".indexOn":["author", "vegitable"],
                   "author": {
                   ".read": true,
                     ".write": true
                    },
                   "vegitable": {
                   ".read": true,
                     ".write": true
                    }
               } 
             },
    "NewData": {
          ".read": true,
          ".write": true
      },
      "data1": {
           ".read": true,
           ".write": true
      },
      "data2": {
        ".indexOn": "child2",
          "child2": {
            ".read":true,
            ".write":true
          }
      }
    }
}
====================== Output

======================== Pushing data ================================
 My s_json string is : {"NewData":"Kaboom2"}
Done setString /NewData:Kaboom2
Done second level setString /:{"post":{"post1":{"author":"TOM","vegitable":"tomato"},"post2":{"author":"JERRY","vegitable":"potato"}}}
/
json
{"data1":"value0","data2":{"child2":"_value1"}}
{"data1":"value0", "data2":{"child2":"_value1"}}
======================== Get specific data ================================
Got NewData as: Kaboom2
{"child2":"_value1"}
Value extracted from json key value pair: _value1
======================== query specific second level data ================================
{}
Posted Value extracted from vegitable: 
======================== Pushing data ================================
 My s_json string is : {"NewData":"Kaboom3"}
Done updateNode {"NewData":"Kaboom3"}
/
json
{"data1":"value1","data2":{"child2":"_value2"}}
{"data1":"value1", "data2":{"child2":"_value2"}}
======================== Get specific data ================================
Got NewData as: Kaboom3
{"child2":"_value2"}
Value extracted from json key value pair: _value2
======================== query specific second level data ================================
{}
Posted Value extracted from vegitable: 
======================== Pushing data ================================
 My s_json string is : {"NewData":"Kaboom4"}
Done updateNode {"NewData":"Kaboom4"}
/
json
{"data1":"value2","data2":{"child2":"_value3"}}
{"data1":"value2", "data2":{"child2":"_value3"}}
======================== Get specific data ================================
Got NewData as: Kaboom4
{"child2":"_value3"}
Value extracted from json key value pair: _value3
======================== query specific second level data ================================
{}
Posted Value extracted from vegitable: 
======================== Pushing data ================================
 My s_json string is : {"NewData":"Kaboom5"}
Done updateNode {"NewData":"Kaboom5"}
/
json
{"data1":"value3","data2":{"child2":"_value4"}}
{"data1":"value3", "data2":{"child2":"_value4"}}
======================== Get specific data ================================
Got NewData as: Kaboom5
{"child2":"_value4"}
Value extracted from json key value pair: _value4
======================== query specific second level data ================================
{}
Posted Value extracted from vegitable: 
======================== Pushing data ================================
 My s_json string is : {"NewData":"Kaboom6"}
Done updateNode {"NewData":"Kaboom6"}
/
json
{"data1":"value4","data2":{"child2":"_value5"}}
{"data1":"value4", "data2":{"child2":"_value5"}}
======================== Get specific data ================================
Got NewData as: Kaboom6
{"child2":"_value5"}
Value extracted from json key value pair: _value5
======================== query specific second level data ================================
{}
Posted Value extracted from vegitable: 
======================== Pushing data ================================
 My s_json string is : {"NewData":"Kaboom7"}
Done updateNode {"NewData":"Kaboom7"}
/
json
{"data1":"value5","data2":{"child2":"_value6"}}
{"data1":"value5", "data2":{"child2":"_value6"}}
======================== Get specific data ================================
Got NewData as: Kaboom7
{"child2":"_value6"}
Value extracted from json key value pair: _value6
======================== query specific second level data ================================
{}
Posted Value extracted from vegitable: 
======================== Pushing data ================================
 My s_json string is : {"NewData":"Kaboom8"}
Done updateNode {"NewData":"Kaboom8"}
/
json
{"data1":"value6","data2":{"child2":"_value7"}}
{"data1":"value6", "data2":{"child2":"_value7"}}

`

mobizt commented 5 years ago

I can't test your code and db rules as it depends on your objectives.

Why don't try to understand what you see the error result (in case of query) that is the message that returns from server?

Try to add same level children nodes in db rules first e.g. ["aaa/111", "aaa/222", "aaa/333"...], and test it. If it works, try to add one different level children nodes which mixed together e.g. ["aaa/111", "aaa/222", "aaa/111/AAA", "aaa/333"...] .

This can make you better understand what's the google designed approaches and its limitations when there is no document available.

usrdes commented 5 years ago

I tried to look up on stackoverflow, many others are having similar issues (not from ESP32 use cases, but from other type of coding). Google's documentation and examples are not clear. I experimented as you indicated, but without success. Only time it works, is if I query on the value, of the key-value pair. But this should not be the case, as the code does not know ahead of time what the value of the key-value pair is.

in the above case, I am trying to use only the keys. It works for only 1 level deep key value pair. However, if I restructure the database to have only one level key-value pairs, I need a way to connect data associated with one value of one key-value pair with the remaining key-value pairs to pick and choose only what I am looking for in the database. Otherwise, I end up downloading the entire database, causing unacceptable amounts of data transfers for large data bases.

Anyway, I will continue to look.

Also just wondering, if you plan to upgrade this library to include Googles new "firestore" data base which appears to be moving in the right direction with appropriate server based noSQL , query searches.

Once again, thank you for taking the time to share this excellent library

mobizt commented 5 years ago

Thanks for your info. About Firestore, I will upgrade the library to support it later after I finished working with incomming library update.

I successfully make the library to create both Firebase ID and OAth2.0 access tokens which is the most importance that enables ESP32 to access all the Google services REST APIs included Firestore.

New library is now supported Google Cloud Storage.

usrdes commented 5 years ago

Hi Mobizt,

OK. I need to tell you the following: I used another library with the same database as I show above. (details, including code and output below) However, this new library consistently works. Perhaps, you want to check why my code above fetched an empty json using your library?

I like your library, and want to use it, because you do more error checks and also provide diagnostic info if things go wrong...

https://github.com/ioxhop/IOXhop_FirebaseESP32

--------------------------- Using IOXhop_FirebaseESP32 library -----------


#include <WiFi.h>
#include <IOXhop_FirebaseESP32.h>
#include "mycredentials.h"

// Set these to run example.
#define FIREBASE_HOST MYHOST
#define FIREBASE_AUTH MYDBSECRET
#define WIFI_SSID mySSID
#define WIFI_PASSWORD myPWD

void setup() {
  Serial.begin(115200);
  // connect to wifi.
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("connecting");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("connected: ");
  Serial.println(WiFi.localIP());
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);

}

void GetSpecificSecondLevelData() {
    JsonObject& mData = Firebase.get("post/post2");
    if (!Firebase.failed()) {
      Serial.println("Got json:");
      mData.printTo(Serial);
      Serial.println("");
      const char* val = mData["vegitable"];
      Serial.print("Value extracted from vegitable: ");
      Serial.println(val);
  } else {
      //Failed, then print out the error detail
      Serial.println("Firebase get failed");
      Serial.println(Firebase.error());
  }
}
void loop() {
  Serial.println("In Loop");
  GetSpecificSecondLevelData();
  delay(5000);
} 
 --------------------------------------- Output
In Loop
Got json:
{"author":"JERRY","vegitable":"potato"}
Value extracted from vegitable: potato
In Loop
Got json:
{"author":"JERRY","vegitable":"potato"}
Value extracted from vegitable: potato
.
.
.
mobizt commented 5 years ago

You apply the query in my library and other library is not implement this.

Query is the data filter which you choose to do in that way the result will be empty if you chose the inappropriate query parameters which not depends on index on. Index is need when you want to query faster with large set of data which google need to let user choose few of data to be indexed which not the way you are trying to do with many nodes.

If you read the query document, you can find the better way to filter your data with storing virtual node ".priority" with your data which the priority node is invisible in Console.

Priority is the new feature available and you can check the priority example which I already add it.

Again as I states in readme doc, my library returns server payload instead of casting other datatypes from string as user want to do. You will not know actually data was

usrdes commented 5 years ago

OK, Thank you, Your documentation is also good!. Looking forward to your adding Firestore. Best wishes