-1

I'm making an app that periodically uploads your step data to Firestore for tracking purposes, and I add all the step data with date:steps in key:value pairs of a hashmap, but for some reason, each individual key:value pair goes into a nested map as value with the key as some arbitrary number. I can't figure out why this is happening. Here's the code:

        val stepMap = HashMap<String, String>()
        for (dp in dataSet.dataPoints) {

            Log.i(TAG,"Data point:")
            Log.i(TAG,"\tType: ${dp.dataType.name}")
            Log.i(TAG,"\tStart: ${dp.getStartTimeString()}")
            Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}")
            for (field in dp.dataType.fields) {
                Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
                stepMap.put("${dp.getStartTimeString()}", "${dp.getValue(field)}")

            }
        }

        db.collection("users")
            .document(""+GoogleSignIn.getLastSignedInAccount(this).email)
            .update(LocalDateTime.now().toString(), stepMap)

This is what the data looks like on Firestore: error on Firebase

I need it to look like this: what it should look like

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
anon
  • 1
  • 1
  • What do you want it to look like? [Useful reference](https://firebase.google.com/docs/firestore/manage-data/add-data) – Tyler V Sep 15 '21 at 17:16
  • Edited the post. – anon Sep 15 '21 at 17:29
  • Have you tried using set with merge instead of update? Update with nested maps tries to do extra logic for nested fields. [relevant Q](https://stackoverflow.com/questions/47295541/cloud-firestore-update-fields-in-nested-objects-with-dynamic-key) and [refs](https://firebase.google.com/docs/firestore/manage-data/add-data?authuser=0#update_fields_in_nested_objects). If there is a dot in the timestamp (e.g. for milliseconds) it may be trying to read that as "dot notation" – Tyler V Sep 15 '21 at 17:40
  • Would probably also help if you printed out the values of `LocalDateTime.now().toString()` and `stepMap` and added those to the question. – Tyler V Sep 15 '21 at 17:46
  • The value of LocalDateTime.now().toString() atm is ```2021-09-16T00:47:44```. stepMap is really weird - it only shows the last value (e.g., ```{2021-09-10T08:46:10=24}```) if I log it after the loop finishes, but when it uploads, all values upload. – anon Sep 15 '21 at 19:20
  • I am unable to replicate this. I made a simple test program and ran `document("test").update(uploadTime, data)` where I just hard-coded both of those variables (string and map) and I get the expected structure. Try making a minimal example where you just manually define `stepMap` with a few values and hard-code the uploadTime string and if the behavior continues, add that to the question. – Tyler V Sep 16 '21 at 02:39
  • I get the expected structure if map is already defined and values aren't added to it during the loop. If I define some values in the map already, e.g., key1:value, key2:value2, then those values are individually added to each nested map as well. – anon Sep 16 '21 at 02:54
  • It sounds like the problem is from how you're building the map then, not with firebase at all. If you print out the full map (`println("map = ${stepMap}")`) just before the firebase call what does it show? – Tyler V Sep 16 '21 at 02:57
  • It only shows the last element of the map - ```{2021-09-11T11:27:08=45}``` – anon Sep 16 '21 at 02:59
  • That doesn't sound right. Can you make a minimal problem that reproduces that behavior? In your code above, the `map.put(key,val)` call looks like it uses the same key for each field too, so that's just going to overwrite the values. – Tyler V Sep 16 '21 at 03:01
  • I'm not sure what's happening so I wouldn't be able to reproduce that behaviour - I don't know where the randomly generated integers like 413, 425, etc. are coming from and why the stepMap doesn't have other values. The call, however, doesn't use the same key for each field, since a new date string is generated for each value. – anon Sep 16 '21 at 03:46
  • `stepMap.put("${dp.getStartTimeString()}", "${dp.getValue(field)}")` how is that not getting the same start time string for each field? Probably should add the output from your Log.i calls to the question. – Tyler V Sep 16 '21 at 03:50
  • Each datapoint has a separate start time (using the Fitness API), so it's not the same key. From the log, two of the start times are: ```2021-09-14T16:24``` and ```2021-09-15T11:09:52``` So the keys are different. – anon Sep 16 '21 at 05:17
  • Yes, but you call that multiple times per data point (once per field)... You need to post the logs, print out (in code) the keys and values you are assigning (and update the code in the question to match what you print), and ideally convert this to a minimal example someone else could run. At this point nobody will be able to help you without more details. – Tyler V Sep 16 '21 at 13:17
  • Also, `LocalDateTime.now()` should produce a string with milliseconds (e.g. `2019-01-21T05:47:08.644`). Print this out *in your code* to confirm. This is almost certainly why you are getting those extra fields - the update routine takes the `.` in the key name to indicate a nested map. – Tyler V Sep 16 '21 at 13:21

2 Answers2

0

Try =>

db.collection("users")
.document(""+GoogleSignIn.getLastSignedInAccount(this).email)
.update(LocalDateTime.now().toString(), FieldValue.arrayUnion(stepMap))
charlie.7
  • 329
  • 3
  • 5
0

Assuming that you have a database structure that looks like this:

Firestore-root
  |
  --- users (collection)
       |
       --- $email (document)
             |
             --- UploadTime (Map)
                   |
                   --- StartTime1: "value1"
                   |
                   --- StartTime2: "value2"
                   |
                   --- StartTime3: "value3"

To update the values of StartTime1, StartTime2 and StartTime3 that exist under the UploadTime property, please use the following lines of code:

db.collection("users")
    .document(""+GoogleSignIn.getLastSignedInAccount(this).email)
    .update(
            "UploadTime.StartTime1", "value4",
            "UploadTime.StartTime2", "value5",
            "UploadTime.StartTime3", "value6"
    ).addOnCompleteListener(/* ... /*);

The result in your database will be:

Firestore-root
  |
  --- users (collection)
       |
       --- $email (document)
             |
             --- UploadTime (Map)
                   |
                   --- StartTime1: "value4"
                   |
                   --- StartTime2: "value5"
                   |
                   --- StartTime3: "value6"

Since it's an update operation, all the other fields in the document will remain untoched.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193