5

Updating a field contains period (.) is not working as expected. In docs, nested fields can be updated by providing dot-seperated filed path strings or by providing FieldPath objects. So if I have a field and it's key is "com.example.android" how I can update this field (from Android)?

In my scenario I've to set the document if it's not exists otherwise update the document. So first set is creating filed contains periods like above and then trying update same field it's creating new field with nested fields because it contains periods.

db.collection(id).document(uid).update(pkg, score)

enter image description here

Prasanna
  • 526
  • 7
  • 18

5 Answers5

14

What you want to do is possible:

FieldPath field = FieldPath.of("com.example.android");
db.collection(collection).document(id).update(field, value);
Feffer
  • 153
  • 1
  • 7
  • In case someone is searching how to do this in the Firestore JS SDK, this is how it is possible (split the paths and create a FieldPath instance): `const fieldPath = new FieldPath('com', 'example');` `await docRef.update(fieldPath, FieldValue.delete());` – sceee May 19 '20 at 10:05
3

This is happening because the . (dot) symbol is used as a separator between objects that exist within Cloud Firestore documents. That's why you have this behaviour. To solve this, please avoid using the . symbol inside the key of the object. So in order to solve this, you need to change the way you are setting that key. So please change the following key:

com.example.android

with

com_example_android

And you'll be able to update your property without any issue. This can be done in a very simple way, by encoding the key when you are adding data to the database. So please use the following method to encode the key:

private String encodeKey(String key) {
    return key.replace(".", "_");
}

And this method, to decode the key:

private String decodeKey(String key) {
    return key.replace("_", ".");
}

Edit:

Acording to your comment, if you have a key that looks like this:

com.social.game_1

This case can be solved in a very simple way, by encoding/decoding the key twice. First econde the _ to @, second encode . to _. When decoding, first decode _ to . and second, decode @ to _. Let's take a very simple example:

String s = "com.social.game_1";
String s1 = encodeKeyOne(s);
String s2 = encodeKeyTwo(s1);
System.out.println(s2);
String s3 = decodeKeyOne(s2);
String s4 = decodeKeyTwo(s3);
System.out.println(s4);

Here are the corresponding methods:

private static String encodeKeyOne(String key) {
    return key.replace("_", "@");
}

private static String encodeKeyTwo(String key) {
    return key.replace(".", "_");
}

private static String decodeKeyOne(String key) {
    return key.replace("_", ".");
}

private static String decodeKeyTwo(String key) {
    return key.replace("@", "_");
}

The output will be:

com_social_game@1
com.social.game_1 //The exact same String as the initial one

But note, this is only an example, you can encode/decode this key according to the use-case of your app. This a very common practice when it comes to encoding/decoding strings.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • 3
    Yes currently I'm doing the same, but it's not actual solution, because key may contains '_' or any special character that are replaced while encoding then decoding will not give the same. – Prasanna Apr 04 '18 at 10:23
  • Only the `.` (dot) should be replaced. This is causing problems. In the way you encode the key, in the same way you decode the key. The key before encoding and they key after decoding will **always** be the same. Why are you saying that is not? Provide me an example. But remember, using the `.` will not help you at all. – Alex Mamo Apr 04 '18 at 10:31
  • Let's say my key is "com.social.game_1". Now encode and decode, you will not get the same. – Prasanna Apr 04 '18 at 15:24
  • You're right but please see my updated answer that solves your case. It's ok now? – Alex Mamo Apr 04 '18 at 15:41
  • Is there everything alright? Have you tried my solution above, does it work? – Alex Mamo Apr 05 '18 at 06:29
  • Still if I have @ symbol in my key. key is String it can have any character. – Prasanna Apr 05 '18 at 11:30
  • You're right when you say it can have any character but you need to skip only those characters that can cause you troubles. I just gave you an example. In this case store as key only the characters that are accepted. Or you can simply replace the characters so you can achieve what you want. Where is the problem? I don't understand you. Where are you stuck? – Alex Mamo Apr 05 '18 at 11:35
  • It's not the solution since we are replacing (.) with another character, we don't know whether the replaced character is already exists in the string. Since my key is dynamic I can't predict what characters it contains. – Prasanna Apr 05 '18 at 11:41
  • You cannot predict what characters will contain but it doesn't matter at all since only the `.` (dot) character can cause you troubles. And this is the only character that need to be replaced. For example, `@` is allowed. So for example, a key like this `name@email_com`, will work perfectly fine. `@` does not need to be replaced. So you can use dynamic keys as long as you don't use `.` periods. – Alex Mamo Apr 05 '18 at 11:55
0

Best way to overcome this behavior is to use the set method with a merge: true parameter.

Example:

db.collection(id).document(uid).set(new HashMap<>() {{
  put(pkg, score); 
}}, SetOptions.merge())
marson
  • 913
  • 10
  • 32
0

for the js version

firestore schema:

 cars: {
   toyota.rav4: $25k
 }

js code

  const price = '$25k'
  const model = 'toyota.rav4'
  const field = new firebase.firestore.FieldPath('cars', model)
  return await firebase
    .firestore()
    .collection('teams')
    .doc(teamId)
    .update(field, price)
not_fubar_yet
  • 194
  • 15
-1

Key should not contains periods (.), since it's conflicting with nested fields. An ideal solution is don't make keys are dynamic, those can not be determined. Then you have full control over how the keys should be.

Prasanna
  • 526
  • 7
  • 18
  • That's not correct. You can use dynamic keys as long as you don't use periods. The `.` (dot) symbol is used as a separator between objects that exist within Cloud Firestore documents. Any other symbols can be used without any problems. – Alex Mamo Apr 05 '18 at 11:57