is it good or bad practice to publish 2 find querys inside the one Meteor.isServer function inside my collection?
I have this code: deals.js / collection
Meteor.publish('deals', function () {
return Deals.find({ userId: this.userId });
});
And I'd like to add another publication like so:
if (Meteor.isServer) {
Meteor.publish('deals', function () {
return Deals.find({ userId: this.userId });
});
Meteor.publish('deals', function () {
return Deals.find({ category: 'technology });
});
}
The reason for the 2nd publication is too enable a category component where only that category of results are displayed.
So now I can subscribe to this inside my component createContainer. Thanks!
There is nothing wrong in itself having more than 1 publication from the same Collection.
However, in your case, I am not sure using the same 'deals' identifier for the Meteor publication is a good idea.
If your publications serve different purposes (typically they are used at different times / in different components), then simply use different names / identifiers.
But if I understand correctly, you actually want to use them in the same component, so that it receives Deals documents that are either from the current user or from a given category? In that case, simply use a MongoDB query selector with $or:
Meteor.publish('deals', function () {
return Deals.find({
$or: [{
userId: this.userId
}, {
category: 'technology'
}]
});
}
Or even return an array of cursors:
Meteor.publish('deals', function () {
return [
Deals.find({ userId: this.userId }),
Deals.find({ category: 'technology' })
];
}
(note that this also enables you to publish documents from different Collections in a single publication!)
Related
I have an Ionic app, on the service I declared a function which is supposed to obtain multiple collections from a Firestore Database and must return all of them.
I have managed to get one collection on the service section like this:
read_Divisions() {
return this.firestore.collection('divisions').snapshotChanges();
}
And here is the the page typescript
ngOnInit() {
this.crudService.read_Divisions().subscribe(data => {
this.Divisions = data.map(e => {
return {
Name: e.payload.doc.data()['name'],
};
})
});
}
This is my idea of the service function for multiple collections:
read_Divisions() {
let divisions = this.firestore.collection('divisions').snapshotChanges();
let teams = this.firestore.collection('teams').snapshotChanges();
return [divisions,teams];
}
the way I obtain one collection on the page doesn't seem to be easily applicable for an array. What's the best way to go about it?
You can use forkJoin to wait for multiple observables, in that case making two separate queries, so probably the code would look like this:
readDivisions() {
return forkJoin({
divisions: this.firestore.collection('divisions').snapshotChanges(),
teams: this.firestore.collection('teams').snapshotChanges()
});
}
in another place in your code you would subscribe to readDivisions:
readDivisions.subscribe(result => console.log(result));
it will be smth like:
{divisions: [], teams: []}
I'm creating a StencilJS app (no framework) with a Google Firestore backend, and I want to use the RxFire and RxJS libraries as much as possible to simplify data access code. How can I combine into a single observable stream data coming from two different collections that use a reference ID?
There are several examples online that I've read through and tried, each one using a different combination of operators with a different level of nested complexity. https://www.learnrxjs.io/ seems like a good resource, but it does not provide line-of-business examples that make sense to me. This question is very similar, and maybe the only difference is some translation into using RxFire? Still looking at that. Just for comparison, in SQL this would be a SELECT statement with an INNER JOIN on the reference ID.
Specifically, I have a collection for Games:
{ id: "abc000001", name: "Billiards" },
{ id: "abc000002", name: "Croquet" },
...
and a collection for Game Sessions:
{ id: "xyz000001", userId: "usr000001", gameId: "abc000001", duration: 30 },
{ id: "xyz000002", userId: "usr000001", gameId: "abc000001", duration: 45 },
{ id: "xyz000003", userId: "usr000001", gameId: "abc000002", duration: 55 },
...
And I want to observe a merged collection of Game Sessions where gameId is essentially replace with Game.name.
I current have a game-sessions-service.ts with a function to get sessions for a particular user:
import { collectionData } from 'rxfire/firestore';
import { Observable } from 'rxjs';
import { GameSession } from '../interfaces';
observeUserGameSesssions(userId: string): Observable<GameSession[]> {
let collectionRef = this.db.collection('game-sessions');
let query = collectionRef.where('userId', '==', userId);
return collectionData(query, 'id);
}
And I've tried variations of things with pipe and mergeMap, but I don't understand how to make them all fit together properly. I would like to establish an interface GameSessionView to represent the merged data:
export interface GameSessionView {
id: string,
userId: string,
gameName: string,
duration: number
}
observeUserGameSessionViews(userId: string): Observable<GameSessionView> {
this.observeUserGameSessions(userId)
.pipe(
mergeMap(sessions => {
// What do I do here? Iterate over sessions
// and embed other observables for each document?
}
)
}
Possibly, I'm just stuck in a normalized way of thinking, so I'm open to suggestions on better ways to manage the data. I just don't want too much duplication to keep synchronized.
You can use the following code (also available as Stackblitz):
const games: Game[] = [...];
const gameSessions: GameSession[] = [...];
combineLatest(
of(games),
of(gameSessions)
).pipe(
switchMap(results => {
const [gamesRes, gameSessionsRes] = results;
const gameSessionViews: GameSessionView[] = gameSessionsRes.map(gameSession => ({
id: gameSession.id,
userId: gameSession.userId,
gameName: gamesRes.find(game => game.id === gameSession.gameId).name,
duration: gameSession.duration
}));
return of(gameSessionViews);
})
).subscribe(mergedData => console.log(mergedData));
Explanation:
With combineLatest you can combine the latest values from a number of Obervables. It can be used if you have "multiple (..) observables that rely on eachother for some calculation or determination".
So assuming you lists of Games and GameSessions are Observables, you can combine the values of each list.
Within the switchMap you create new objects of type GameSessionView by iterating over your GameSessions, use the attributes id, userId and duration and find the value for gameName within the second list of Games by gameId. Mind that there is no error handling in this example.
As switchMap expects that you return another Observable, the merged list will be returned with of(gameSessionViews).
Finally, you can subscribe to this process and see the expected result.
For sure this is not the only way you can do it, but I find it the simplest one.
I want to select data from 1 room, but I also want the ids of all other rooms.
I do it with
const roomId = req.params.roomId;
Room.findById(roomId).then(room => {
if (room) {
Room.find({}).sort({ createdAt: 1 }).then(rooms => {
if (rooms) {
res.render()
}
}).catch(next);
}
}).catch(next);
but this results in 2 database calls.
Is it possible to limit it to only 1 call?
The room I want has a lot of data which I don't need to extract for the other rooms since I only need their IDs.
Get all the rooms by .find() and then use underscore library's findWhere function to filter what you want out of complete dataset. The underscore library works very well for large datasets also.
Ideally the code should look like below:
Room.find({}).sort({ createdAt: 1 }).then(rooms => {
if (rooms) {
var filteredRoom = _.findWhere(rooms, {_id: roomId})
filteredRoom = filteredRoom.pop()
res.render()
}
}).catch(next);
I need to access collection on client-side javascript files to sort data and perform various operations. Currently I'm just using find() or findOne(), but it's very inconsistent and sometimes doesn't return value quickly enough so it doesn't work.
Am I doing it the right way? Are there any other options provided to retrieve and manipulate collection data?
For example, I want to find this data:
data = Stats.findOne({
name: Session.get('name')
}, {sort: {date: -1}});
variable = data.variable;
And then I want to use variable to filter collection:
ranking = Stats.find({
variable: variable
}, {sort: {score: -1}}).fetch();
I can't find a reason why it doesn't work.
Meteor is reactive, which means that as that Stats collection populates with data from the server, code that uses it will be automatically re-run. If you're trying to display the data on screen, you want to use a helper, a reactive variable, and the autorun function.
JS:
Template.yourTemplate.onCreated( function() {
var template = this;
template.variable = new ReactiveVar( null );
template.autorun( function() {
var result = Stats.findOne({ name: Session.get('name') }, { sort: { date: -1 } });
if( result && result.variable ) {
template.variable.set( result.variable );
}
});
});
Template.yourTemplate.helpers({
ranking() {
return Stats.find({ variable: Template.instance().variable.get() }, { sort: { score: -1 } }).fetch();
}
});
HTML:
<template name="yourTemplate">
{{#each rank in ranking}}
Do stuff with {{rank}}
{{/each}}
</template>
This will ensure that your reactive variable changes as Stats.findOne() of your session variable changes, and that your template can appropriately get the rankings you want.
I have an application that uses Flow Router and its pub/sub mechanics. I also have a collection and template helpers. The code is, on client
Template.theCase.helpers({
theCase: function () {
var id = FlowRouter.getParam('id');
var theCase = Cases.findOne({
id: id
});
return theCase;
}
});
and
{{#with theCase}}
{{ id }}
{{/with}}
then, on server
Meteor.publish('theCase', function (id) {
return Cases.findOne({
id: id
});
});
and finally, on both (lib)
FlowRouter.route('/case/:id', {
subscriptions: function (params) {
this.register('theCase', Meteor.subscribe('theCase', params.id));
},
action: function (params, queryParams) {
return BlazeLayout.render('container');
}
});
The problem, as I see it, is that helper returns undefined, since it's not allowed to find items in a collection by any other property than _id. How can I overcome it? I've read truckload of the official docs on pub/sub, helpers and routing, and I just can't find the solution. Any suggestions?
You can query by any field. The helper returns undefined because it didn't find anything that matched.
This code is problematic:
Meteor.publish('theCase', function (id) {
return Cases.findOne({
id: id
});
});
It should be: return Cases.find({id: id});
Publications must return a cursor or call this.ready()