VueFire: Resolving references after setting on the client - firebase

According to the docu "[...] VueFire will automatically bind up to one nested references.". That works well, if I retrieve an object (map) from the database with a property being a ref: The ref gets ressolved automatically on the client (ref_property will not hold the path to the object (e.g. users/123) but the actual data ({username: 'john', hometown: 'autumn'}).
Question is: How do I update a ref_property (e.g. suppose a last_edit_by_ref) on the client in a way, that a.) VueFire is able to resolve this to a valid JSON for the UI and b.) make sure that it's stored as a ref in the database at the same time?
I tried to fetch the referenced object (again) from the collection as explained here ("To write a reference to a document, you pass the actual reference object"). The issue with this is however, that VueFire does not ressolve this, leading to empty values in the UI:
post.last_edit_by_ref = db.collection('users').doc('123')
Background: If I'm setting plain JSON, the property is no longer stored as a reference in the database. This is bad, since the linked object is likely to be changed (and the linking objekt would then hold copied, outdated data).

It does not related to VueFire. It is how firebase parse the Object it get in the set/update methods.
If you focus on this part:
const data = {
age: 18,
name: "John",
carRef: db.collection('cars').doc('john-car'),
}
await db.collection('users').doc('john').set(data);
You will have the ref in firestore. And in turn, VueFire will automatic bind the object.
For your case, i think you will need to find a way to get the db.collection('users').doc(last_edit_user_id) to make the ref for post.

Related

StorageReference updateMetadata overwrites all properties

I got a problem with Firebase Storage, updateMetadata() function. I want to add custom Metadata, but updateMetadata overwrites all properties. I use Firebase Storage on Flutter. Here is the code:
StorageReference storageReference = _firebaseStorage.ref().child('filename');
storageReference.updateMetadata(StorageMetadata(customMetadata: {'receiver': 'ID'}));
I just want to add the receiver with the given ID, but every other property like content-type is being overwritten. Is this a common issue or is there a different way to solve this problem?
Regards, Tom
The documentation says:
You can update file metadata at any time after the file upload completes by using the updateMetadata() method. Refer to the full list for more information on what properties can be updated. Only the properties specified in the metadata are updated, all others are left unmodified.
So what you're seeing is not the expected behavior as far as I can tell. I can reproduce it, filed a bug report on the FlutterFire repo.
Until that bug is fixed, if you want to modify the metadata, you can work around the bug with these steps:
Read the existing metadata
Make the change to the values you read
Write the updated, complete metadata back

Firestore references create a "TypeError: Converting circular structure to JSON"

I'm using Firebase's Firestore inside a Vue JS app:
"firebase": "^5.8.0",
"vue-firestore": "^0.3.16",
When I try fetching a document which has a field referencing another document (reference type in Firestore), I get the following error:
[Vue warn]: Error in render: "TypeError: Converting circular structure to JSON"
Whenever I change the type of that field in the document to a string, it seems to work fine.
I understand this is because of something in the Firestore JS SDK that is trying to serialize the document (and a bunch of metadata coming with the document) to JSON and there is a circular reference somewhere?
In my data's structure and fields I do not have a circular reference. It's simply one field that is referencing another document, and the referenced document has no more references to any other document.
My code for fetching the data is:
methods: {
getContent() {
const db = this.$firebase.firestore();
db
.collection('places')
.doc(this.$route.params.placeKey)
.orderBy('name')
.get()
.then(snap => {
this.places = []
snap.forEach(doc => {
this.places.push(doc.data())
})
})
}
So my questions are:
1) First of all, am I doing something incorrectly in my code? Even if there was no error being raised, would Firestore's JS SDK resolve the reference for me? Or would I have to call the reference and resolve it myself to get the referenced document's data?
2) What's the best practice with Firestore when you have documents you can be referenced? Should you use references? When? When would you denormalize?
Thanks!
To answer to your second question about storing References: actually, at the time of writing this answer, there is no real advantage of storing References (i.e. path elements) as Reference type instead of storing them as String.
Please watch this official video from Firebase https://www.youtube.com/watch?v=Elg2zDVIcLo&t=274s which contains detailed explanations (starting at 4:34).

How to remove Cloud Firestore field type specifiers when using REST API?

I totally made up the name "type specifiers." What I mean is the stringValue key in front of a value. Usually I would expect a more-standard response: "name" : "name_here".
{
"fields": {
"name": {
"stringValue": "name_here"
}
}
}
Is it possible to remove those when making a GET call?
More importantly, it be nice to understand why it's structured like it is. Even for POST-ing data? The easy answer is probably because Cloud Firestore, unlike Realtime Database, needs to know the specific types, but what are all the deeper reasons? Is there an "official" name for formatting like this where I could do more research?
For example, is the reasoning any related to Protocol Buffers? Is there a way to request a protobuf instead of JSON?
Schema:
Is it possible to remove those when making a GET call?
In short No. The Firestore REST API GET returns an instance of Document.
See https://firebase.google.com/docs/firestore/reference/rest/v1beta1/projects.databases.documents#Document
{
"name": string,
"fields": {
string: {
object(Value)
},
...
},
"createTime": string,
"updateTime": string,
}
Regarding the "Protocol Buffer": When the data is deserialized you could just have a function to convert into the structure you wish to use, e.g. probably using the protocol buffers if you wish but as there appear to be libraries for SWIFT, OBJECTIVE-C, ANDROID, JAVA, PYTHON, NODE.JS, GO maybe you won’t need to use the REST API and craft a Protocol Buffer.
Hopefully address your “More Importantly” comment:
As you eluded to in your question Firestore has a different data model to the Realtime Database.
Realtime database data model allows JSON objects with the schema and keywords as you want to define it.
As you point out, the Firestore data model uses predefined schemas, in that respect some of the keywords and structure cannot be changed.
The Cloud Firestore Data Model is described here: https://firebase.google.com/docs/firestore/data-model
Effectively the data model is / where a document can contain a subcollection and the keywords “name”, “fields”, “createdTime”, “upTime” are in a Firestore document (a pre-defined JSON document schema).
A successful the Firestore REST API GET request results in a Document instance which could contain collection of documents or a single document. See https://firebase.google.com/docs/firestore/reference/rest/. Also the API discovery document helps give some detail about the api:
https://firestore.googleapis.com/$discovery/rest?version=v1beta1
An example REST API URL structure is of the form:
https://firestore.googleapis.com/v1beta1/projects/<yourprojectid>/databases/(default)/documents/<collectionName>/<documentID>
It is possible to mask certain fields in a document but still the Firestore Document schema will persist. See the three examples GET:
collection https://pastebin.com/98qByY7n
document https://pastebin.com/QLwZFGgF
document with mask https://pastebin.com/KA1cGX3k
Looking at another example, the REST API to run Queries
https://firebase.google.com/docs/firestore/reference/rest/v1beta1/projects.databases.documents/runQuery
the response body is of the form:
{
"transaction": string,
"document": {
object(Document)
},
"readTime": string,
"skippedResults": number,
}
In summary:
The Realtime database REST API will return the JSON for the object according to the path/nodes as per your “more-standard response”.
The Firestore REST API returns a specific Firestore predefined response structure.
There API libraries available for several language so maybe it’s not necessary to use the REST API and craft your own Protocol Buffer but if you needed to you it’s probably feasible.
I don't understand why somebody just say that you can't and don't try think some solution for help! Seriously that this is a really problem solver?
Anyway, I created a script that will help you (maybe it's late now hahaha).
The script encode json and after replace it as string to modify and remove Google type fields (low process).
It's a simple code, I know that you can improve it if necessary!
WARNING!!
Maybe you will have problems with values that contain '{}' or '[]'. This can be solved with a foreach that convert all strings that contains this elements in other char (like '◘' or '♦', some char that you know that doesn't will be in value.
Ex.: Hi {Lorena}! ------> Hi ◘Lorena♦!
After the process, convert again to '{}' or '[]'
YOU CAN'T HAVE FIELDS WITH THE SAME NAME THAT GOOGLE FIELDS
Ex.: stringValue, arrayValue, etc
You can see and download the script in this link:
https://github.com/campostech/scripts-helpers/blob/master/CLOUD%20STORE%20JSON%20FIELDS%20REMOVER/csjfr.php

Does entry to DynamoDB depends on the model class of you application?

I have a model class which has four member variables like this:
A.java
1.Attribute-1
2.Attribute-2
3.Attribute-3
4.Attribute-4
Now I make entries to the DynamoDB and the entries go successful and I can see the entries going.
Now I make few changes in the model and add 2 more attributes.
5.Attribute-5
6.Attribute-6
And in the application I am setting the values of these 2 attributes there.
Now when I try to insert some entry into the application then I am getting this error.
com.amazonaws.services.dynamodbv2.model.AmazonDynamoDBException: The provided key element does not match the schema (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException;
What is the error
I believe you are using DynamoDBMapper class in AWS SDK JAVA with save() method.
1) Firstly, yes, you need to set the key values in the model object when you perform the update on existing item. save() performs either create or update the item
2) Secondly, save method has a save behavior config. You need to set the behavior accordingly based on your use case
public void save(T object,
DynamoDBSaveExpression saveExpression,
DynamoDBMapperConfig config)
UPDATE (default) : UPDATE will not affect unmodeled attributes on a
save operation and a null value for the modeled attribute will remove
it from that item in DynamoDB. Because of the limitation of updateItem
request, the implementation of UPDATE will send a putItem request when
a key-only object is being saved, and it will send another updateItem
request if the given key(s) already exists in the table.
UPDATE_SKIP_NULL_ATTRIBUTES : Similar to UPDATE except that it ignores
any null value attribute(s) and will NOT remove them from that item in
DynamoDB. It also guarantees to send only one single updateItem
request, no matter the object is key-only or not.
CLOBBER : CLOBBER
will clear and replace all attributes, included unmodeled ones,
(delete and recreate) on save. Versioned field constraints will also
be disregarded. Any options specified in the saveExpression parameter
will be overlaid on any constraints due to versioned attributes.
Reference link
Sample Code:-
Movies movies = new Movies();
//setting the partition and sort key
movies.setYearKey(1999);
movies.setTitle("MyMovie BOOL");
//New attribute added
movies.setProducts(Stream.of("prod1").collect(Collectors.toSet()));
DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(dynamoDBClient);
//Set the save behavior, so that it wouldn't impact the existing attributes. Refer the full definition above
dynamoDBMapper.save(movies, SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES.config());

Firebase Reference not returned after add

I do something like this:
var temp = $scope.names.$add({"firstname" : $scope.firstname, "lastname" : $scope.lastname, "school" : $scope.selectedSchool});
I get a returned object, but when I try
$log.log(temp.toString());
I get just the object, whereas the documentation for firebase says I should see the path to the string of my new firebase URL. I also tried
$log.log(temp.name());
And same result, is this related to the implementation of those "functions" in angularfire, or to actual firebase itself?
I'm trying to syncronize adding "names" to a names portion of my json object, and then following it up with creating a section in my json object where I can get more details of the individual and so I want to match up the key's
kinda like this JSON Data structure resulting in the least passed data from database
I don't see how to get the above to work, and I saw (at one point, can't find the link now), that firebase was recommending using transaction not the "once" operation, so I was hoping I would get the individuals key, then pass that into creating a new firebase "subobject?" that matched it.

Resources