Update same client collection with different subscription - meteor

I have a problem with subscriptions which should have fill same collection on the client with the different set of records.
For example:
I have a Books collection and two different publications:
Meteor.publish(‘books’, () => Books.find({ status: { $ne: 3 } });
publish “booksForReservation” which is returning an array of books (books are filtered based on the reservation and some other data)
Problem occurs on the client, when I coming from one route to another. All Books collection are in the main component, and when I need those booksForReservation collection on the client is not updating with only that specific set of books.
I have subscribed on the client like:
Meteor.subscribe(‘booksForReservation’, reservationsIds);
let books = Books.find({}).fetch();
but I’m still getting all books collection displayed. when I have filtered Books collection on the client side with the same query used on the server, collection is updated. But then filtering it on the server doesn’t have any point.
How can I update same collection with different subscription?

You have to filter collection on the client side with a query from the server. You subscribe to two publications, so on the client you will have data from both of them. The point of filtering collection on the server side is security. By doing it you won't publish any unwanted data to the client. You can read more about it here https://guide.meteor.com/data-loading.html#specific-queries

Related

Adding a new field to nested subcollection in firebase [duplicate]

I have two collections. One collection "User", who contains the user info (username...) And one collection "Post" who contains all posts of my flutter application. A post document contains a "Text Post" and the "Username" of the writter. I add an option in my application to allow the user to change his nickname every 6 months. But I must change the username in the "User" collection and in all posts it creates in the "Post" collection. What is the best practice ?
The user make a query for update username in "User" collection, i intercept the "OnUpdate" in cloud function and i update all post in server side.
The user make a query for update username in "User" collection, and update all "Post" collection in client side.
I guess if i do a geDocuments () there is a limit, so I should do it in multiple times if I have too many "Post" Documents, am I correct?
There is no singular best practice here. Both approaches you describe are valid, and neither is pertinently better than the other.
A few things to keep in mind in either scenario:
You may not be able to handle all updates in a single batched write (since that can handle at most 500 documents at once), so I'd recommend not wasting energy on that.
In some scenario's it is also acceptable (and sometimes even required) to not update the existing documents, so I recommend always considering that too.

Can I query for documents and filter some document fields out of the querySnapshot response using Firestore?

Imagine that I have a collection of documents:
collection:
'products'
And inside that collection I have documents, and each document has a really heavy field that I want filtered before it gets sent to my clients.
document product1:
{
id: 'someUniqueKey',
title: 'This is the title',
price: 35.00,
heavyField: [
'longStringxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'longStringxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'longStringxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'longStringxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'longStringxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'longStringxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
Can I query for those documents inside the products collection and leave this specific heavyField out of my response?
I would like to be able to get the documents with 1 of their fields filtered out. Maybe select the fields I want to receive on my client.
Is this possible? Or in this case it's best to structure my data different and leave the heavyField in a different collection?
In SQL, this would be called a "projection" in a query. Cloud Firestore doesn't support projections from web and mobile client SDKs. You should instead put your "heavy" fields into documents in another collection, and query that only as needed.
If you're wondering why, it partly has to do with the way that the client local persistence layer works. It would make caching much more complicated if only certain fields existed locally for a given document. It's much more straightforward to simply fetch and store the entire document, so there is no question whether or not the document is locally available.
For the purpose of data modeling, it's best to think of documents as "atomic units", all or nothing, that can't be broken down.

Firestore, fetch only those documents from a collection which are not present in client cache

I am implementing a one-to-one chat app using firestore in which there is a collection named chat such that each document of a collection is a different thread.
When the user opens the app, the screen should display all threads/conversations of that user including those which have new messages (just like in whatsapp). Obviously one method is to fetch all documents from the chat collection which are associated with this user.
However it seems a very costly operation, as the user might have only few updated threads (threads with new messages), but I have to fetch all the threads.
Is there an optimized and less costly method of doing the same where only those threads are fetched which have new messages or more precisely threads which are not present in the user's device cache (either newly created or modified threads).
Each document in the chat collection have these fields:
senderID: (id of the user who have initiated the thread/conversation)
receiverID: (id of the other user in the conversation)
messages: [],
lastMsgTime: (timestamp of last message in this thread)
Currently to load all threads of a certain user, I am applying the following query:
const userID = firebase.auth().currentUser.uid
firebase.firestore().collection('chat').where('senderId', '==', userID)
firebase.firestore().collection('chat').where('receiverId', '==', userID)
and finally I am merging the docs returned by these two queries in an array to render in a flatlist.
In order to know whether a specific thread/document has been updated, the server will have to read that document, which is the charged operation that you're trying to avoid.
The only common way around this is to have the client track when it was last online, and then do a query for documents that were modified since that time. But if you want to show both existing and new documents, this would have to be a separate query, which means that it'd end up in a separate area of the cache. So in that case you'll have to set up your own offline storage on top of Firestore's, which is more work than I'm typically willing to do.

Linked collections in Meteor

I am working on a card game platform with a lot (10,000+) of cards dynamically updated with real-world data. Cards are populated/updated once a day.
I have two basic collections at the foundation (asides from users):
1) data - all individual items with different data values for same data fields/parameters (for example, various car models with their specifications). I update this collection once a day from a json API I have on another server for another purpose.
2) cards - "printed" cards with unique IDs but duplicates are off course possible (so we can have 10 Ford Focus 2010. cards).
Cards collection has a couple of most important fields from data collection (model, brand, top performance parameter(s) of the card) to provide efficient user card browsing, and a "dataId" field which links it to data collection for detailed info.
Cards in collection "cards" should be inserted ("issued" or "printed") with functions/methods on server side but in response to client side events (such as new-game etc). When a new card is inserted/dispatched, it first gets a unique "admin-owner" with a user _id from users table for one-to-one relationship, which is later updated to create ownership.
So, on client side, cards collection is like a user "deck" (all cards where owner is user). If I am correct, it should be written on the server side as:
Meteor.publish('cards', function() {
return Cards.find({"userID":this.userId});
});
This is all quite clear and up to that point Meteor is fantastic as it saves me months of work!
But, I am not sure about:
1) I would like to have a client-side data collection publication to cover client detailed card view (by linking cards with data). It should off course have only all data items from data collection with details for each card in client card collection ("deck"). I see it as something like:
Meteor.publish('data', function (dataIds *array with all unique data item ids in client card collection *) {
return Data.find("dataID":{$in:dataIds);
});
2) I need a server/client method to add/insert new cards from data-items ("create 10 news Ford Focus 2010 cards") with an empty/admin user by executing Meteor.call methods from client console of "admin" user, and a server/client method to change ownership of a random card so that it becomes a part of a client cards collection ("cast random card to user").
Where would I place those methods? How can I access server methods from client console (if a certain admin user is logged)?
4) I need a clever way of handling a server publication/client subscription of data collection that will have only the data used in cards from client cards collection.
Should I use client side minimongo query to create an array with all dataIds needed to cover local cards collection? I am new to mongo, so I am not sure how would I write something like SELECT DISTINCT or GROUP BY to get that. Also, not sure if that is the best way to handle that, or should I do something server side as a publication?
Having a clear idea on 1-4 would get me going and then I guess I would dig my way around (and under :)
1) The publish function you wrote makes perfect sense. Of course, there's a bit confusion in the term "client-side data collection publication": publications are on the server side, while on the client side you've got subscriptions. Also, while you didn't specify your schema, I suppose you've got dataID field in cards collection, that joins with _id in data collection, so your find should say {_id: {$in: dataIds}}.
2) Read this carefully, there's all you need for that. Remember to check user privileges within the server side method. A rule of thumb for security is that you should never trust the client.
3) There's no point 3?
4) I'm not sure how the question here is different from 1. However, you should probably familiarize with this method, which you can use in your subscription to ensure the _ids in the array are unique.

Meteor and subscriptions

I don't understand the concept of Meteor.subscribe.
It is supposed to receive records from the server, and attach it to collections with the same name, right?
[subscribe] will queue incoming attributes until you declare the Meteor.Collection on the client with the matching collection name.
So, why the example in docs uses different names? What is the relation between allplayers and players?
Meteor.subscribe("allplayers");
...
// client queues incoming players records until ...
...
Players = new Meteor.Collection("players");
There are two names:
The name of the collection ('players' in this case).
The name of the subscription ('allplayers' in this case).
A subscription is a way to get records into a client side collection. The name this collection that the records go into is decided (on the server side) by the use of this.set() in the relevant Meteor.publish function, but usually it is just name of the collection that is queried on the the server side[1].
Many subscriptions can deposit data into the same collection, so certainly the name of the subscription doesn't need to correspond to the name of the collection. In fact, it's probably only a good idea to have them be the same if you are doing a fairly straightforward single subscription to that collection.
[1] If you return a cursor (e.g. return players.find();) in Meteor.publish, it automatically wires up calls to this.set(name) for you, where name is inferred from the server side players collection.

Resources