I'm trying to promote a content on a page. This content should the upcoming content, i.e. the next one in all of the same type ("event").
Here's how metadata look like :
---
layout: event
date: 2013-12-11
title: Cool event
---
The docpad.coffee file collections configuration looks like (using moment.js):
collections:
events: (database) ->
database.findAllLive(type: "event", [date: -1])
next_events: (database) ->
#getCollection("events")
.setFilter("upcoming", (event, searchString) ->
return moment(event.eventDate, "YYYY-MM-DD").diff(moment()) < 0
)
Finally in the index.html.eco :
<% for event in #getCollection("next_events").toJSON(): %>
<h3><%= event.title %></h3>
<% end %>
Problem: it shows all my events, not only the future ones. When I use the events collection it works as intended (all events ordered by date desc). But no difference when I use the next_events collection. I know that to show only one I'll need to reduce the collection content with something like <% for event in #getCollection("next_events").toJSON()[0]: %>.
Any pointer greatly appreciated.
Try this:
next_events: (database) ->
#getCollection("events")
.createLiveChildCollection()
.setFilter("upcoming", (event, searchString) ->
return moment(event.eventDate, "YYYY-MM-DD").diff(moment()) < 0
)
.query()
Related
I have a query that provides 'feed' data to a collection view using RxSwift and RxFirebase.
This feed data now has a 'Privacy' field (possible values: "Public", "Sub", and "Private") and I need to filter out the 'Private' entities.
However when I add a 'Where' clause to do this, the listener no longer adds newly posted entities from this collection. The first call to this function always has the 'listens' bool set to true, because it wants to listen for new entities posted/deleted by a user.
I do not know why the events do not trigger the query.
Here is the current query code:
func fetchGlobalFeed(limit: Int, startAfter: Timestamp?, listens: Bool) -> Observable<[Entity.Fragment]> {
var query = db.collection(k.mCollection)
.limit(to: limit)
.order(by: "PublishedAt", descending: true)
if let timestamp = startAfter {
query = query.start(after: [timestamp])
}
let observable = listens ? query.rx.listen() : query.rx.getDocuments()
return observable
.catchErrorJustComplete()
.map(FirestoreHelpers.dataFromQuerySnapshot)
.map { data in
data.compactMap {
try? FirestoreDecoder()
.decode(Entity.Fragment.self, from: $0)
}
}
}
And changing the query to:
var query = db.collection( k.mCollection )
.limit( to: limit )
.whereField( "Privacy", in: ["Public", "Sub"] ) // <- this causes issue with listener
.order( by: "PublishedAt", descending: true )
Maybe an explanation of how listeners work and any suggestions would be appreciated.
Thanks.
RxFirebase version: 0.3.8
*Edit:
I don't have enough Rep to post pictures which is weird because I used to be able to. (says it requires 10 rep pts)
Here is the debug output:
2020-09-10 11:37:03.101: MagmaFirestoreProvider.swift:165
(fetchGlobalFeed(limit:startAfter:listens:)) -> subscribed
2020-09-10 11:37:03.324: MagmaFirestoreProvider.swift:165
(fetchGlobalFeed(limit:startAfter:listens:)) -> Event
next([Entity.Fragment(ref: <FIRDocumentReference: 0x60000109ef40>,
publisher: Optional(UserEntity.Fragment(ref: <FIRDocumentReference:
0x60000109ee80>, username: "Art_Collective", name: nil .........
privacy: Optional(magma.MagmaMagPrivacy.Public))])
2020-09-10 11:37:03.458: MagmaFirestoreProvider.swift:165
(fetchGlobalFeed(limit:startAfter:listens:)) -> Event completed
2020-09-10 11:37:03.458: MagmaFirestoreProvider.swift:165
(fetchGlobalFeed(limit:startAfter:listens:)) -> isDisposed
When a new item is posted to the feed with a privacy of "Sub", the item does not appear and there is zero debug output.
I have to refresh the collection view to get the above output and the new item in the feed.
I assume that's because the observable is being disposed?
As suggested in the comments above, I created a composite index for the query in the Firebase console.
I will note that it was important to have the index structure a certain way.
I tried creating an index with the following settings:
Collection ID : 'mags'
Field 1: 'PublishedAt' : 'Descending'
Field 2: 'Privacy' : 'Descending'
(note: firebase docs say to use either 'Ascending' or 'Descending'
when you're using the 'in' clause even if you're not ordering on this
field, it will not affect the equality of the query results)
This index did not work for some reason, I'm not sure why...
However, I realized other indexes created by a dev before me were mechanically similar but structured differently. So I mirrored that with the following:
Collection ID : 'mags'
Field 1: 'Privacy' : 'Ascending'
Field 2: 'PublishedAt' : 'Descending'
And it worked!
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
I am building some little log reporting with meteor.
have a dead simple table, that should contain all the data that received from the external mongodb server.
the html:
<tbody>
{{#each impressions}}
<tr>
{{> impression}}
</tr>
{{/each}}
</tbody>
js :
Meteor.subscribe('impressions');
...
...
Template.logResults.helpers({
'impressions': function() {
var sTs = Router.current().params.fromTs;
var eTs = Router.current().params.toTs;
return Impressions.find({});
}
});
So far, so good. BUT, when I am changing the query to this one :
Impressions.find({
$and: [{
ts: {
$gte: sTs
}
}, {
ts: {
$lte: eTs
}
}]
});
The results aren't displayed on the HTML DOM,
I tried to debug that, and created a console.log of this exact query,
and surprisingly all the correct results return successfully to the console.
screenshot attached.
I am probably doing something wrong, maybe with publish/subscribe thing.
help someone?
Thanks.
P.S. I removed the insecure and auto-publish,
have this code on the server folder
Meteor.publish('impressions', function() {
return Impressions.find();
});
and this code on the main lib folder
Impressions = new Mongo.Collection("banners");
enter image description here
The router stores the parameters for the current route as strings (which makes sense because URLs are strings), so you need to explicitly convert the values to integers before querying the database. Give something like this a try:
var sTs = Number(Router.current().params.fromTs);
var eTs = Number(Router.current().params.toTs);
Notes:
parseInt or parseFloat may be a better choice depending on the nature of your input. See this question for more details.
You could do the type conversion in the router itself and pass the values in the data context to the helpers. See this section of the iron router guide.
I suspect it worked when you typed it into the console because you used numbers instead of strings. E.g. ts: {$gte: 123} instead of ts: {$gte: '123'}.
I have a courses collection
{courses{{course1_title,id,author,pages[{title,content,model, etc...},
[ etc...]{course2,....}}
I'm trying to display the current page's data inside the courses collection through the router
this.route('page', {
path: '/:title',
data: function() {
return courses.findOne({'pages.title':this.params.title};
}
});
I would like to display the current page's data like this:
<template name="page">
<div class ="container page">
<h1>Page</h1>
<!--display current page data -->
<h3>{{title}}</h3>
<div>{{ an other content}} etc..... </div>
</div>
For now, the router returns the entire course's data and the title displayed is the course's title. I don't find how to access the current page's data in order to display it in the page's template.
I tried
return courses.findOne({'pages.title':this.params.title}{fields:{{'pages.title':this.params.title}:1}}
and a lot of other ways. I didn't find it.
What is the right way?
Your current query will search all the courses for a matching page title, then return the entire course (with all the pages)
You should return only the data for the page in question:
course = courses.findOne({pages: { $elemMatch: { title: this.params.title }});
return course.pages[0]
As an aside it's probably better to create a separate collection for pages (with each page linking back to the course id). Although that's less "Mongo", Meteor can only operate reactively over collections, not sub-documents.
As I am new to programming and Meteor I am currently building a (simple) Quizz app using Meteor.js. I followed the discover Meteor Guide book and rebuilding their example microscope project into a quiz. I am currently struggling with retrieving the array of questions from the mongo db and displaying just one of them within the app.
The data within my collection currently looks like this:
Quizzes.insert(
{"quiztitle": "Quiz One",
"quizquestions": ["Q1.1", "Q1.2"]
}),
I am currently displaying all of them thorugh
<template name="quizPage">
<h3>
{{#each quizquestions}}
{{> quizQuestion}}
{{/each}}
</h3>
and
<template name="quizQuestion">
<div class="quiz">
<div class="quiz-content">
{{this}}
</div>
</div>
I have tried several solutions already to getting only the first question to display:
1.Substituting the array number through a helper function with Spacebars. Although the helper worked (it returned a specific number for instance 0), and the array by itself ( 0 between brackets). Meteor does not seem to allow spacebar inserts into array brackets.
<template name="quizQuestion">
<div class="quiz">
<div class="quiz-content">
{{quizquestions.[{{questionnumber}}]}}
</div>
</div>
2.aReturning a specific field through a mongodb query. for example
Return Quizzes.find( { quiztitle: 'Quiz One' }, { quizquestion: 1, _id:0, quiztitle: 0 });
Unfortunately this is only allowed on the server side. I have also tried to save the array resulting from the return into a global variable within the lib folder
questionArray = Quizzes.find( { quiztitle: 'Quiz One' }, { quizquestion: 1, _id:0, quiztitle: 0 } );
This is also the case when I try slicing the collection, which is suggested in a different post
3.As this also does not seem to work I have tried publishing a subset of the collection for use in a specific quiz. The problem I have here is that the collection seems to be published in its entirety. I publish the collection on the frontpage.js through
Meteor.subscribe('quizzes');
I have also tried subscribing within an autorun as is suggested in the documentation at http://docs.meteor.com/#meteor_subscribe
Deps.autorun(function () {
Meteor.subscribe("quizzes")});
Question: Could you help me find a way to return only the questions array and either save it to a global variable or return it through a helper. I hope you can help me out, thanks a lot,
Meteor Beginner.
First, you need to make sure the data is available on the client. In chrome, open up your javascript console (cmd+option+j) and paste Quizzes.find().fetch() and you should see your objects. Assuming that is good, continue...
To get your questions to display, you can return the specific question to a {{#with}} block like this:
{{#with question}}
<li>{{this}}</li>
{{/with}}
Your question helper could look something like this...
Template.TEMPLATE_NAME.helpers({
question: function(){
var currentQuestion = Session.get('currentQuestion') || 0;
return Quizzes.findOne({}).quizquestions[currentQuestion]
}
});
Then you can increment the Session variable each time you answer or go to the next question in a Meteor event, something like this:
Template.TEMP_NAME.events({
'click .next-question': function(){
var num = Session.get('currentQuestion') + 1;
Session.set('currentQuestion', num);
}
});
This will cause the helper to rerun and your new question will be passed back to the {{#with}} block.
In the quizPage template, add a helper which finds a single question:
Template.quizPage.firstQuestion = function() {
return this.quizquestions[0];
}
Then use it:
<template name="quizPage">
<h3>
<!-- #with is like #each, but for a single item -->
{{#with firstQuestion}}
{{> quizQuestion}}
{{/with}}
</h3>
</template>