Check if a field exists in a document snapshot firestore - firebase

I have a MessageModel in which there are a few fields. But the field "edited" does not exist in the document of each message. It is a new field that I want to add later in the future. When I get all messages using stream it throws the error.
Unhandled Exception: Bad state: field does not exist within the DocumentSnapshotPlatform
Is there any way I can check if the field "edited" exists in the model or ignore it?
This is my MessageModel:
factory MessageModel.fromJson(DocumentSnapshot snapshot) => MessageModel(
chatId: snapshot["chat_id"],
messageId: snapshot["message_id"],
userId: snapshot["user_id"],
you: snapshot["you"],
time: snapshot["time"],
seen: snapshot["seen"],
type: snapshot["type"],
message: snapshot["message"],
fileURL: snapshot["file_url"] ?? "" ,
thumbnail: snapshot["thumbnail"] ?? "" ,
isUploading: RxBool(false),
isPlaying: RxBool(false),
file: File("").obs,
thumb: Uint8List(5).obs,
edited: snapshot["edited"]
);
I am using this stream to get all my messages from firestore. Or can I check here if the field exists?
Flutter code:
Stream<List<MessageModel>> getMessages() {
Stream<QuerySnapshot> stream =
CollectionReferences.chatRef.doc(userID.value).collection("messages").orderBy("time", descending: true).snapshots();
return stream.map((data) => data.docs.map((e) => MessageModel.fromJson(e)).toList());
}

If you capture the data as a map, you can check if the map contains a key:
edited: (snapshot.data() as Map<String, dynamic>).containsKey("edited") ?
snapshot["edited"] : null

Related

Flutter Cloudfirestore: how to check whether field exists

My Flutter app uses Firbase Cloudfirestore as its backend. Later I'll want to add new features to my app which would require new fields in a Firestore document. How can I check whether the field exists in the document and return a default value if it doesn't?
Stream<List<Car>> streamCars() {
return _carsCollection.snapshots().map((snapshot) => snapshot.docs.map((document) => Car.fromDocumentSnapshot(document)).toList());
}
static Car fromDocumentSnapshot(DocumentSnapshot snapshot) {
return Car(
id: snapshot.id,
date: snapshot['date'] ?? Timestamp.now(),
seats: snapshot['seats'] ?? 0,
newFeature: snapshot['newFeature'] ?? '', // This field does not exist yet and throws error
);
}
This throws the error:
Bad state: field does not exist within the DocumentSnapshotPlatform
You can do it like so.
newFeature: (snapshot.data() as Map)['newFeature'] ?? ''
static Car fromDocumentSnapshot(DocumentSnapshot snapshot) {
return Car(
id: snapshot.id,
date: snapshot['date'] ?? Timestamp.now(),
seats: snapshot['seats'] ?? 0,
newFeature: snapshot['newFeature']==null ?'tjhjhjh': '', // check it if is null will solve the error
);
}

The images are being stored in firestore but still in the browser its showing firebase error (Unsupported field value: undefined)?

Firebase error (unhandled rejection)
React error on the browser:
Unhandled Rejection (FirebaseError): Function addDoc() called with invalid data. Unsupported field value: undefined (found in field username in document posts/MkHzQWzXyayty0KfQQgP)
Most probably the problem is in this code :
// complete function
storage
.ref("images")
.child(image.name)
.getDownloadURL()
.then(url => {
// Post image on db
db.collection("posts").add({
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
caption: caption,
imageUrl: url,
username: username
});
setProgress(0);
setCaption("");
setImage(null);
});
}
);
}; ```
In the error, it says your variable username is undefined. You should make sure that it has a value. Meanwhile, you can do a quick and dirty fix like this:
db.collection("posts").add({
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
caption: caption,
imageUrl: url,
username: username || null,
});
This code will assign null to username when the variable username is undefined.

Do CosmosDB Mongo API compound unique indexes require each field to be unique?

I'm trying to set up a collection of versioned documents in which I insert a new document with the same id and a timestamp whenever there's an edit operation. I use a unique compound index for this on the id and timestamp fields. CosmosDB is giving me MongoError: E11000 duplicate key error whenever I try to insert a document with a different id but an identical timestamp to another document. The MongoDB documentation says that I should be able to do this:
https://docs.mongodb.com/v3.4/core/index-unique/#unique-compound-index
You can also enforce a unique constraint on compound indexes. If you use the unique constraint on a compound index, then MongoDB will enforce uniqueness on the combination of the index key values.
I tried using a non-unique index but the Resource Manager template failed, saying that non-unique compound indexes are not supported. I'm using the node.js native driver v3.2.4. I also tried to use Azure Portal to insert documents but received the same error. This makes me believe it's not a problem between CosmosDB and the node.js driver.
Here's a small example to demonstrate the problem. I'm running it with Node v10.15.3.
const { MongoClient } = require('mongodb');
const mongoUrl = process.env.COSMOSDB_CONNECTION_STRING;
const collectionName = 'indextest';
const client = new MongoClient(mongoUrl, { useNewUrlParser: true });
let connection;
const testIndex = async () => {
const now = Date.now();
connection = await client.connect();
const db = connection.db('master');
await db.collection(collectionName).drop();
const collection = await db.createCollection(collectionName);
await collection.createIndex({ id: 1, ts: -1 }, { unique: true });
await collection.insertOne({ id: 1, ts: now, title: 'My first document' });
await collection.insertOne({ id: 2, ts: now, title: 'My other document' });
};
(async () => {
try {
await testIndex();
console.log('It works');
} catch (err) {
console.error(err);
} finally {
await connection.close();
}
})();
I would expect the two insert operations to work and for the program to exit with It works. What I get instead is an Error:
{ MongoError: E11000 duplicate key error collection: master.indextest Failed _id or unique key constraint
at Function.create (/home/node/node_modules/mongodb-core/lib/error.js:43:12)
at toError (/home/node/node_modules/mongodb/lib/utils.js:149:22)
at coll.s.topology.insert (/home/node/node_modules/mongodb/lib/operations/collection_ops.js:859:39)
at handler (/home/node/node_modules/mongodb-core/lib/topologies/replset.js:1155:22)
at /home/node/node_modules/mongodb-core/lib/connection/pool.js:397:18
at process._tickCallback (internal/process/next_tick.js:61:11)
driver: true,
name: 'MongoError',
index: 0,
code: 11000,
errmsg:
'E11000 duplicate key error collection: master.indextest Failed _id or unique key constraint',
[Symbol(mongoErrorContextSymbol)]: {} }
Is this expected behavior or a bug in CosmosDB's MongoDB API?

Flutter: The method forEach isn't defined for the class DataSnapshot

I need to iterate a node in a DatabaseReference in firebase. But it is wired that there is no forEach function in DataSnapshot that is in firebase_database library!
I also tried to use DataSnapshot object that is in firebase library (that has a forEach function in it) but I got a error:
[dart] The argument type '(DataSnapshot) → List<dynamic>' can't be assigned to the parameter type '(DataSnapshot) → FutureOr<dynamic>'.
And here is my code:
getAccountsList() {
return firebaseDbService.getAccounts().once().then((DataSnapshot snapshot) {
var list = [];
snapshot.forEach((DataSnapshot account) => list.add({
'id': snapshot.key,
'name': snapshot.child('name').val(),
}));
return list;
});
}
It's unclear what you are trying to do in your code, both child(String path) and val() do not exist in the class DataSnapshot, you can check here:
https://github.com/flutter/plugins/blob/master/packages/firebase_database/lib/src/event.dart#L27
Also you cannot loop like this:
for( var values in snapshot.value){
print("Connected to second database and read ${values}");
}
since you will get the following error:
which means also you cannot use forEach() on the snapshot to iterate.
Let's say you have this database, and you want to get the names:
user
randomId
name: John
randomId
name: Peter
You need to do the following:
_db=FirebaseDatabase.instance.reference().child("user");
_db.once().then((DataSnapshot snapshot){
Map<dynamic, dynamic> values=snapshot.value;
print(values.toString());
values.forEach((k,v) {
print(k);
print(v["name"]);
});
});
Here the reference points toward the node users, since snapshot.value is of type Map<dynamic,dynamic> then you are able to do this Map<dynamic, dynamic> values=snapshot.value;.
Then you loop inside the map using forEach() to be able to get the keys and values, you will get the following output:
This line I/flutter ( 2799): {-LItvfNi19tptjdCbHc3: {name: peter}, -LItvfNi19tptjdCbHc1: {name: john}} is the output of print(values.toString());
Both the following lines:
I/flutter ( 2799): -LItvfNi19tptjdCbHc3
I/flutter ( 2799): -LItvfNi19tptjdCbHc1
are the output of print(k);
The other two lines are the output of print(v["name"]);
To add the names into a list do the following inside the forEach():
list.add(v["name"]);
print(list);

Property 'subscribe' does not exist on type 'AngularFireList<{}>'

I'm trying to run a chat app using ionic and I'm getting this message
[19:55:51] typescript: src/pages/chat/chat.ts, line: 26
Property 'subscribe' does not exist on type 'AngularFireList<{}>'.
L25: this.username = this.navParams.get('username');
L26: this._chatSubscription = this.db.list('/chat').subscribe( data => {
L27: this.messages = data;
[19:55:51] typescript: src/pages/chat/chat.ts, line: 37
Property 'catch' does not exist on type 'PromiseLike<void>'.
L36: // message is sent
L37: }).catch( () => {
L38: // some error. maybe firebase is unreachable
can anybody help me?
You need to specify valueChanges() or snapshotChanges() before you subscribe.
valueChanges() Returns an Observable of data as a synchronized array of JSON objects. All Snapshot metadata is stripped and just the method provides only the data.
snapshotChanges() Returns an Observable of data as a synchronized
array of AngularFireAction[].
You can read more about retrieving data here
So your code should look like this:
this.db.list('chat').valueChanges().subscribe(data => {
this.messages = data;
});

Resources