Vuexfire:How many reads does bindFirestoreRef trigger? - vuexfire

Since I observe an unexpectedly high number of reads I tried to figure out using the docu how many reads binding the same document several times triggers.
Suppose I have an action:
export const setTodosRef = firestoreAction(
({ bindFirestoreRef, unbindFirestoreRef }, documentId) => {
bindFirestoreRef('documents', db.collection('documents').doc('sameid'))
}
)
What happens, if I call setTodosRef 3 times with same document id 'sameid'? Is it 3 or 1?
Is vuexfire smart enough to know that the same document is already bound?

Related

Dart/Flutter stream to a field/data in firebase [duplicate]

How can I listen to a specific field change with firestore js sdk ?
In the documentation, they only seem to show how to listen for the whole document, if any of the "SF" field changes, it will trigger the callback.
db.collection("cities").doc("SF")
.onSnapshot(function(doc) {
console.log("Current data: ", doc && doc.data());
});
You can't. All operations in Firestore are on an entire document.
This is also true for Cloud Functions Firestore triggers (you can only receive an entire document that's changed in some way).
If you need to narrow the scope of some data to retrieve from a document, place that in a document within a subcollection, and query for that document individually.
As Doug mentioned above, the entire document will be received in your function. However, I have created a filter function, which I named field, just to ignore document changes when those happened in fields that I am not interested in.
You can copy and use the function field linked above in your code. Example:
export const yourCloudFunction = functions.firestore
.document('/your-path')
.onUpdate(
field('foo', 'REMOVED', (change, context) => {
console.log('Will get here only if foo was removed');
}),
);
Important: The field function is not avoiding your function to be executed if changes happened in other fields, it will just ignore when the change is not what you want. If your document is too big, you should probably consider Doug's suggestion.
Listen for the document, then set a conditional on the field you're interesting in:
firebase.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(word).collection('Pronunciations').doc('Castilian-female-IBM').onSnapshot(function(snapshot) {
if (snapshot.data().audioFiles) { // eliminates an error message
if (snapshot.data().audioFiles.length === 2) {
audioFilesReady++;
if (audioFilesReady === 3) {
$scope.showNextWord();
}
}
}
}, function(error) {
console.error(error);
});
I'm listening for a document for a voice (Castilian-female-IBM), which contains an array of audio files, in webm and mp3 formats. When both of those audio files have come back asynchronously then snapshot.data().audioFiles.length === 2. This increments a conditional. When two more voices come back (Castilian-male-IBM and Latin_American-female-IBM) then audioFilesReady === 3 and the next function $scope.showNextWord() fires.
Just out of the box what I do is watching before and after with the before and after method
const clientDataBefore = change.before.data();
console.log("Info database before ", clientDataBefore);
const clientDataAfter = change.after.data();
console.log("Info database after ", clientDataAfter );
For example now you should compare the changes for a specific field and do some actions or just return it.
Some more about before.data() and after.data() here

MongoDB query: If two docs are referencing each other, eliminate one doc (Keep one combination only)

I have docs like these:
{
_id:61af43169dae3a9c3e133a90
name:"user1",
status: "RECOMMENDED",
recommendedId:61b708b8041895f4c68a3b3d
}
{
_id:61b708b8041895f4c68a3b3d
name:"user2",
status: "RECOMMENDED"
recommendedId:61af43169dae3a9c3e133a90
}
Both users are recommended to each other, so, I don't want both documents having recommended Id populated. I just want one document having recommendedId populated (Keep one combo only)
I would try to prevent this from happening at the time of setting the value of recommendedId in the first place.
So before trying to set the value, you could do something like this:
const idToRecommend = Types.ObjectId()
const recommenders = await Foo.find({
_id: idToRecommend,
recommendedId: user._id
})
if (recommenders.length > 0) {
// We don't want to make the change, we already have a relationship recorded.
}
Cleaning up a db already tainted with these duplicate relationships is a different question, but I would do that as a one-off task rather than a matter of process.

Get number of documents in a big collection in Cloud Firestore

I know this question was already asked but I'm being specific about my case: I've got a large database (approximately 1 million documents inside the collection users).
I wanna get the exact number of documents inside users. I'm trying this:
export const count_users = functions.https.onRequest((request, response) => {
corsHandler(request, response, () => {
db.collection('users').select().get().then(
(snapshot) => response.json(snapshot.docs.length)
)
.catch(function(error) {
console.error("[count_users] Error counting users: ", error);
response.json("Failed");
});
});
});
Although it seems right, it takes forever to give me a result. I'm not allowed to add or remove documents from the database.
Is there any possible approach for getting this quantity?

Non reactive helper and template #each crashing

This is about a problem with a helper containing a dynamic query that involves reactive vars and the $where operator that does not rerun when the reactive vars values are changed. Then about how a try to solve it lead to a strange system behavior and crash.
I have a template in which we want to show a list of found documents inserted within an #each loop:
<template name="todo">
{{#each pendingMTs}}
<button class="showPendMT">
<h4> - <strong> {{name}}</strong> </h4>
</button>
{{/each}}
</template>
The pendingMTs helper searches for the documents in the Tasks collection with a dynamic and somehow elaborate -here simplified- query that involves using the $where operator:
pendingMTs() {
return Tasks.find(
{ $and: [ { owner: Meteor.userId() },
{ time: { $gt: 0 } },
{ $where: function()
{ return moment(this.createdAt).add( (((this.timeuts == 'Days') ? 1 : ((this.timeuts=='Meses') ? 30 : 365)) * this.time), 'days').diff(moment([currYear.get(), currMonth.get()])) < 0; }}
]
});
}
The two reactive vars involved in the search are defined at the creation of the template:
Template.todo.onCreated(function() {
var year = moment().format("YYYY");
var month = moment().format("M");
month = month - 1; //to values from 0 to 11
currYear = new ReactiveVar(year);
currMonth = new ReactiveVar(month);
});
Then in an event handler we modify the reactive vars upon a 'select' change, for instance for the currMonth:
'change #monthForecast' (event) {
event.preventDefault();
const target = event.target;
currMonth.set( target.value );
},
The first issue is that the helper is not rerun despite we modify through the event handler the value of the reactive vars: WHY??
Thinking that this might be due to the fact that the reactive vars are inside the $where function, I added a simple line at the beginning of the helper in order simply to create awareness for them in the helper:
var rerun = currYear.get(); rerun = currMonth.get();
Now certainly that made the helper to rerun any time any of the 2 reactive var was changed. But unfortunately this lead to a very strange behavior:
If the reactive vars were modified, but did not affect the documents retrieved by the helper, system was running fine, but:
When the modified reactive vars caused the helper to retrieve one more document than the number of documents retrieved the first time, the system crashed (and therefore the new document was not shown in the #each template):
Exception from Tracker recompute function: meteor.js:930:11
Error: Bad index in range.getMember: 3
While looking for the cause I found out that the bad index number given, 3 in this case, is always equal to the number of documents retrieved the first time the helper was executed. In this case, after modifying the value of one of the reactive vars, a 4th document had to be shown when system crashed.
I found some maybe related issues on https://github.com/GroundMeteor/db/issues/184
Could anyone point out how this dynamic query involving $where with reactive vars could be done, maintaining its natural reactivity?
Looks like you're not getting and setting your reactive-vars properly. You can't just call myReactiveVar.get() - they are (usually) attached to the template instance so your code should be:
In the pendingMTs helper, you access the instance with const instance = Template.instance(); and then in your $where function you would use instance.currentMonth.get() and instance.currentYear.get()
In the event, the second argument of the event is the template instance, so you would have:
'change #monthForecast' (event, templateInstance) {
event.preventDefault();
const target = event.target;
templateInstance.currMonth.set( target.value );
},
So you're not too far off!
https://docs.meteor.com/api/reactive-var.html
https://themeteorchef.com/tutorials/reactive-dict-reactive-vars-and-session-variables

Mongo - new vs processed approach

I am new to Mongo and have gotten close to where I want to be after 3 days of banging my head against the keyboard, but now I think I may just be misunderstanding certain key concepts:
What I am trying to do:
I have a node script that is pulling in feed items from various sources very frequently and storing them (title, link, origin, processed:false)
I have another script pulling out records at random, one at a time, using them, and updating processed:true
End Goal: Items should be unique by title - if it's been seen before it should not be written to DB, and once it's been processed one time, it should never be processed again.
INSERT SCRIPT:
key = {'title':title};
data = {'origin':origin, 'title':title, 'original_link':original_url, 'processed':false};
collection.update(key, data, {upsert:true}, function(err, doc) { ...
READ SCRIPT:
collection.findOne({processed:false}, function(err, doc){
if (err) throw err;
logger.info("Read out the following item from mongodb:...");
console.dir(doc);
thisId = doc._id;
markProcessed(thisId);
}
var markProcessed = function(id) {
collection.update({ _id:id },
{
$set: {'processed':true},
}, function(err, doc){
if (err) throw err;
logger.info("Marked record:"+id+" as processed");
console.dir(doc);
}
)
};
I've tried using collection.ensureIndex({'title':1}, {unique:true}) to no success either.
As the two scripts run in parallel the read script ends up repeating work on already processed records, and although the markProcessed function was working all yesterday it miraculously does not today :)
I would very much appreciate any guidance.
There is a problem with your insert script. When you use collection.update and you already have a document with the same key in the database, that document will be overwritten with the new one. An unique index doesn't prevent this, because there aren't two documents with the same title in the collection at the same time.
When you don't want to overwrite an existing record, use collection.insert which will fail when the inserted document violates an unique index.