When to use ReactiveDict vs ReactiveVar - meteor

I'm in Meteor and have been using Reactive Var in my project with no issues. In this current example I'm not sure if I need to use a ReactiveDict instead because I'm getting no reactivity with ReactiveVar.
I'm setting the value of the reactiveVar to a mongo query that is resulting in an array of objects.
Template.MasterList.onCreated(function () {
this.teacherList = new ReactiveVar('');
});
Template.MasterList.onRendered(function () {
var weekNum = Session.get('CurrentWeek').substr(0, 1);
var week = 'Week' + weekNum;
var query1 = {};
query1[week] = { $in: ['JC', 'JF', 'F']};
this.teacherList.set(Programs.find({ $and: [{ CampYear: Session.get('GlobalCurrentCampYear') }, query1]}, { sort: { FullName: 1 }}).fetch());
});
One object in the array would look like:
{ ...
Students: {
Week1: {
Monday: ['Joe', 'Mary']
},
Week2: {},
Week3: {}
}
...
}
One of the objects in the array may change to something like:
{ ...
Students: {
Week1: {
Monday: ['Joe', 'Mary', 'Bill']
},
Week2: {},
Week3: {}
}
...
}
When a change to one of the objects happens, the reactiveVar doesn't see the change in the array to re-fire the helper. Is a ReactiveDict to be used here instead?
EDIT:
Helper function:
saturdayTeacher: function (name) {
var weekNum = Session.get('CurrentWeek').substr(0, 1);
var week = 'Week' + weekNum;
var teachers = Template.instance().teacherList.get();
for (var i = 0, len = teachers.length; i < len; i++) {
if (_.contains(teachers[i].Students[week].Saturday, name)) {
Meteor.defer(function () {
i = 0;
});
var teacher = teachers[i].FullName.split(' ');
return teacher[0] + " " + teacher[1].slice(0, 1);
}
}
},

What will help you in a very easy way, is to add an autorun to your onRendered-Function, where you set your ReactiveVar. Because the onRendered-Function is only executed once. When I get it right, you want to listen on changes in the cursor, Session.get('CurrentWeek') and Session.get('GlobalCurrentCampYear').
So try this:
Template.MasterList.onRendered(function () {
var self = this;
this.autorun(function() {
var weekNum = Session.get('CurrentWeek').substr(0, 1);
var week = 'Week' + weekNum;
var query1 = {};
query1[week] = { $in: ['JC', 'JF', 'F']};
self.teacherList.set(Programs.find({ $and: [{ CampYear: Session.get('GlobalCurrentCampYear') }, query1]}, { sort: { FullName: 1 }}).fetch());
});
});
Than it will be executed all the time the result of the query or the Session-Values change.

Related

Google Apps Scripts - How to get key value of document from Firestore?

I'm trying to get the key values of the documents in my Firestore database, but I'm not getting it.
This value below:
This my code:
function objectsToArray(objects) {
var outputArray = [];
for (var i in objects){
outputArray.push([
objects[i].fields.id, objects[i].fields.data, objects[i].fields.acao,
objects[i].fields.categoria, objects[i].fields.movimentos, objects[i].fields.descricao
]);
}
return outputArray;
}
My output Logger.log of JSON:
[20-01-11 19:42:06:370 CET] [{"name":"projects/orcamento- b37bb/databases/(default)/documents/orcamento/0MwgqEm9abho3bpB5yCc","fields":
{"categoria":"SUPERMERCADO","data":"2019-07- 31T00:00:00.000Z","descricao":"","acao":"Despesa","movimentos":23.82,"id":107},
"createTime":"2019-12-31T14:35:47.959299Z","updateTime":"2019-12- 31T14:35:47.959299Z"},
any suggestions?
Thanks
The original data seems to be structured like this:
[
{
"name":"projects/orcamento- b37bb/databases/(default)/documents/orcamento/0MwgqEm9abho3bpB5yCc",
"fields": {
"categoria":"SUPERMERCADO",
"data":"2019-07- 31T00:00:00.000Z",
"descricao":"",
"acao":"Despesa",
"movimentos":23.82,
"id":107
},
"createTime":"2019-12-31T14:35:47.959299Z",
"updateTime":"2019-12- 31T14:35:47.959299Z"
},
You want the value: 0MwgqEm9abho3bpB5yCc
Which is in the element with the name property key.
function objectsToArray(objects) {
var L,nameValue,finalValue;
objects = [
{
"name":"projects/orcamento- b37bb/databases/(default)/documents/orcamento/0MwgqEm9abho3bpB5yCc",
"fields": {
"categoria":"SUPERMERCADO",
"data":"2019-07- 31T00:00:00.000Z",
"descricao":"",
"acao":"Despesa",
"movimentos":23.82,
"id":107
},
"createTime":"2019-12-31T14:35:47.959299Z",
"updateTime":"2019-12- 31T14:35:47.959299Z"
},
]
var outputArray = [];
L = objects.length;
for (var i=0;i<L;i++){
nameValue = objects[i].name;
Logger.log('nameValue: ' + nameValue)
finalValue = nameValue.slice(nameValue.lastIndexOf("/")+1);
Logger.log('finalValue: ' + finalValue)
outputArray.push(finalValue);
}
Logger.log('outputArray: ' + JSON.stringify(outputArray))
return outputArray;
}

Vue doesn't update when computed data change

Context: I have a list of posts with tags, categories from wordpress api. I display these posts with Vue and using computed with a search box to filter the result based on titre, description, tags, and categories
Problem: I am trying to update a computed list when user click on a list of tag available. I add the get and set for computed data like this:
var vm = new Vue({
el: '#blogs',
data: {
search: '',
posts: [],
filterPosts: []
},
beforeMount: function() {
// It should call the data and update
callData();
},
computed: {
filterPosts: {
get: function() {
var self = this;
return self.posts.filter(function(post){
var query = self.search.toLowerCase();
var title = post.title.toLowerCase();
var content = post.content.toLowerCase();
var date = post.date.toLowerCase();
var categories = '';
post.categories.forEach(function(category) {
categories += category.name.toLowerCase();
});
var tags = '';
post.tags.forEach(function(tag){
tags += tag.name.toLowerCase();
});
return title.indexOf(query) !== -1 ||content.indexOf(query) !== -1 || date.indexOf(query) !== -1 || categories.indexOf(query) !== -1 || tags.indexOf(query) !== -1;
});
},
set: function (newValue) {
console.log(newValue);
this.filterPosts = Object.assign({}, newValue);
}
}
},
methods: {
filterByTag: function(tag, event) {
event.preventDefault();
var self = this;
self.filterPosts = self.posts.filter(function(post){
var tags = '';
post.tags.forEach(function(tag){
tags += tag.name.toLowerCase();
});
return tags.indexOf(tag.toLowerCase()) !== -1;
});
}
}
}); // Vue instance
The console.log always output new data based on the function I wrote on methods but Vue didn't re-render the view. I think I didn't do the right way or thought like Vue. Could you please give some insight?
Edit 1
Add full code.
I tried to add filterPosts in data but I received this error from Vue: The computed property "filterPosts" is already defined in data.
Your setter is actually not setting anything, it only logs the new value. You need to store it somewhere.
For example you can store it in the component's data:
data: {
value: 'foo',
},
computed: {
otherValue: {
get() { /*...*/ },
set(newVal) { this.value = newVal },
},
},
But this is definitely not the only possibility, if you use Vuex, the setter can dispatch an action that will then make the computed value get updated. The component will eventually catch the update and show the new value.
computed: {
value: {
get() {
return this.$store.getters.externalData;
},
set(newVal) {
return this.$store.dispatch('modifyingAction', newVal);
},
},
},
The bottomline is you have to trigger a data change in the setter, otherwise your component will not be updated nor will it trigger any rerender.
EDIT (The original answer was updated with full code):
The answer is that unless you want to manually change the list filteredPosts without altering posts, you don't need a get and set function for your computed variable. The behaviour you want can be acheived with this:
const vm = new Vue({
data() {
return {
search: '',
posts: [],
// these should probably be props, or you won't be able to edit the list easily. The result is the same anyway.
};
},
computed: {
filteredPosts() {
return this.posts.filter(function(post) {
... // do the filtering
});
},
},
template: "<ul><li v-for='post in filteredPosts'>{{ post.content }}</li></ul>",
});
This way, if you change the posts or the search variable in data, filteredPosts will get recomputed, and a re-render will be triggered.
After going around and around, I found a solution, I think it may be the right way with Vue now: Update the computed data through its dependencies properties or data.
The set method didn't work for this case so I add an activeTag in data, when I click on a tag, it will change the activeTag and notify the computed filterPost recheck and re-render. Please tell me if we have another way to update the computed data.
var vm = new Vue({
el: '#blogs',
data: {
search: '',
posts: [],
tags: [],
activeTag: ''
},
beforeMount: function() {
// It should call the data and update
callData();
},
computed: {
filterPosts: {
get: function() {
var self = this;
return self.posts.filter(function(post){
var query = self.search.toLowerCase();
var title = post.title.toLowerCase();
var content = post.content.toLowerCase();
var date = post.date.toLowerCase();
var categories = '';
post.categories.forEach(function(category) {
categories += category.name.toLowerCase();
});
var tags = '';
post.tags.forEach(function(tag){
tags += tag.name.toLowerCase();
});
var activeTag = self.activeTag;
if (activeTag !== '') {
return tags.indexOf(activeTag.toLowerCase()) !== -1;
}else{
return title.indexOf(query) !== -1 ||content.indexOf(query) !== -1 || date.indexOf(query) !== -1 || categories.indexOf(query) !== -1 || tags.indexOf(query) !== -1;
}
});
},
set: function (newValue) {
console.log(newValue);
}
}
},
methods: {
filterByTag: function(tag, event) {
event.preventDefault();
var self = this;
self.activeTag = tag;
}
}
}); // Vue instance
Try something like:
data: {
myValue: 'OK'
},
computed: {
filterPosts: {
get: function () {
return this.myValue + ' is OK'
}
set: function (newValue) {
this.myValue = newValue
}
}
}
More:
https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter

FullCalendar RefetchEvents very slow

I call calendar.refetchEvents(); inside an autorun Block to ensure the reactivity of the Scheduler (i'm using resource view) , when tested with big datasets , although i make sure to subscript to only a portion of 2 weeks worth of events , it's extremly slow .
my events arent Json based i'm using meteor and i loop over the events inside the Events function of the calendar .
are there some good fullcalendar practices i'm missing ?
calendar = $('#calendar').fullCalendar({
now: new Date(),
editable: true, // enable draggable events
droppable: true, // this allows things to be dropped onto the calendar
aspectRatio: 1.8,
disableDragging: true,
displayEventTime: false,
selectable:true,
allDaySlot:true,
slotDuration:'24:00',
lazyFetching:true,
scrollTime: '00:00', // undo default 6am scrollTime
header: {
left: 'today prev,next',
center: 'title',
right: 'timelineThreeDays'
},
defaultView: 'timelineThreeDays',
views: {
timelineThreeDays: {
type: 'timeline',
duration: { days: 14 }
}
},
eventAfterAllRender: function(){
Session.set("loading",false);
},
resourceLabelText: 'Employees',
eventRender: function (event, element) {
var originalClass = element[0].className;
if (event.id && event.id.indexOf("temp")>-1){
element[0].className = originalClass + ' dragEvent';
}
else if (event.id && event.id.indexOf("oloc")>-1){
element[0].className = originalClass + ' oloc';
}
else{
element[0].className = originalClass + ' hasmenu';
}
$(element[0]).attr('shift-id', event._id);
element.find('.fc-title').html((event.title?event.title+"<br/>":"")+(event.locationName?event.locationName+"<br/>":"")+moment(event.start).format("HH:mm")+ " "+moment(event.end).format("HH:mm"));
element.bind('mousedown', function (e) {
if (e.which == 3) {
Session.set("selectedShift",event._id);
}
});
},eventAfterRender: function(event, element, view) {
},
resourceRender: function(resourceObj, labelTds, bodyTds) {
var originalClass = labelTds[0].className;
var uid=resourceObj.id;
var resource = Meteor.users.findOne({_id:uid});
if(resource){
var img = Images.findOne({_id: resource.picture});
var imgUrl = img ? img.url() : "/images/default-avatar.png";
var styleString = "<img class='img-profil small' src='"+imgUrl+"'>" + resource.profile.firstname + " " + resource.profile.lastname+" <small style='font-size:0.6em;color:#D24D57;'>"+resource.profile.registeredTelephony+"</small>";
labelTds.find('.fc-cell-text').html("");
labelTds.find('.fc-cell-text').prepend(styleString);
labelTds[0].className = originalClass + ' hasResourceMenu';
}else{
var imgUrl = "/images/default-avatar.png";
var styleString = "<img class='img-profil small' src='"+imgUrl+"'>" + "Unassigned" + " " +" <small style='font-size:0.6em;color:#D24D57;'>"+"</small>";
labelTds.find('.fc-cell-text').html("");
labelTds.find('.fc-cell-text').prepend(styleString);
}
},
resources: function(callback) {
var users = [];
var data = Meteor.users.find({
$or:[
{"profile.showInScheduler":{$exists:false}},
{"profile.showInScheduler":true}
],
assignedTo:{$in:[Session.get("locationId")]},
'locations._id':Session.get("locationId"),
"profile.companyId":Session.get("companyId")
});
var arr = data.map(function(c) {
var employeeType = c.userSettings.employeeType;
var type = EmployeeType.findOne({_id:employeeType});
var img = Images.findOne({_id: c.picture});
var imgUrl = img ? img.url() : "/images/default-avatar.png";
c.name = c.name || "";
var totalHoursAllLocation = 0;
var totalHoursCurrentLocation = 0;
return {
id: c._id,
title: "t"
};
});
arr.push({id:"temp"+Session.get("companyId")+Session.get("locationId"),title:"<div class='img-profil small' style='background: url(/images/default-avatar.png);'></div> UnAssigned"});
callback(arr);
},
events: function(start, end, timezone, callback) {
},
drop: function(date, jsEvent, ui, resourceId) {
// retrieve the dropped element's stored Event Object
var locationId=Session.get("locationId");
var originalEventObject = $(this).data('eventObject');
var copiedEventObject = $.extend({}, originalEventObject);
// assign it the date that was reported
copiedEventObject.start = date;
//copiedEventObject.allDay = allDay;
shift = ShiftTypes.findOne({_id:copiedEventObject.id});
if(shift){
startDate = moment(date);
hour = shift.dayDuration.Start.split(":");
startDate.hours(hour[0]).minutes(hour[1]);
endDate = moment(date);
hour = shift.dayDuration.End.split(":");
endDate.hours(hour[0]).minutes(hour[1]);
if(moment(startDate).isAfter(endDate)){
endDate=endDate.add("1","days");
}
var data = {
shiftId:shift._id,
name:shift.name,
uid:resourceId,
locationId:Session.get("locationId"),
companyId:Session.get("companyId"),
day:date,start:startDate.utc().toDate(),
end:endDate.utc().toDate(),
type:"active"
};
if (SchedulesBeforeInsert(data,"dropActive")){
Schedules.insert(data,function (err,result) {});
}
}
},
eventResize: function( event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view ) {
endDate = moment.utc(new Date(event.start));
schedule = Schedules.findOne({_id:event.id});
var delta = dayDelta._days;
for(i=1;i<delta+1;i++){
Schedules.insert({start:moment.utc(event.start).add(i,"days").toDate(),end:moment.utc(schedule.end).add(i,"days").toDate(),uid:schedule.uid,locationId:Session.get("locationId"),companyId:schedule.companyId,name:schedule.name,shiftId:schedule.shiftId,attendanceCode:schedule.attendanceCode,type:schedule.type});
}
},
dayClick: function(date, jsEvent, view,res,res2) {
Session.set("selectedDay",moment(date).toDate());
Session.set("selectedRessource",res.id);
},
eventClick: function ( event, jsEvent, view ) {
toastr.success(moment(event.start).format('HH:mm') +" TO "+moment(event.endDate).format('HH:mm'))
},
eventReceive: function(event) { // called when a proper external event is dropped
console.log('eventReceive', event);
}
}).data().fullCalendar;
/********************* reactive calendar *****************/
this.autorun(function() {
console.log("autoRun")
Meteor.subscribe("schedules",Session.get("companyId"),moment($('#calendar').fullCalendar('getDate')).startOf('week').toDate(),moment($('#calendar').fullCalendar('getDate')).add(4,"weeks").endOf('week').toDate());
var events = [];
var usersInLocation = Meteor.users.find({$or:[{"profile.showInScheduler":{$exists:false}},{"profile.showInScheduler":true}],assignedTo:{$in:[Session.get("locationId")]},'locations._id':Session.get("locationId"),"profile.companyId":Session.get("companyId")}).fetch();
var userIds = _.pluck(usersInLocation,"_id");
userIds.push("temp"+Session.get("companyId")+Session.get("locationId"));
var data;
if(Session.get("displayAllLocations")===true){
reqEvents = Schedules.find({uid:{$in:userIds},companyId:Session.get("companyId"),type:{$in:["active","activeAttendance"]},start:{$gte:moment(Session.get("currentDate")).startOf('week').toDate()},end:{$lte:moment(Session.get("currentDate")).add(1,"week").endOf('week').toDate()}});
}else{
reqEvents = Schedules.find({uid:{$in:userIds},locationId:Session.get("locationId"),companyId:Session.get("companyId"),type:{$in:["active","activeAttendance"]},start:{$gte:moment(Session.get("currentDate")).startOf('week').toDate()},end:{$lte:moment(Session.get("currentDate")).add(1,"week").endOf('week').toDate()}});
}
reqEvents.forEach(function(evt){
var event = null;
color="";
if(evt.attendanceCode){
attendance =AttendanceCodes.findOne({_id:evt.attendanceCode});
color=attendance.color;
}
attendance = null;
color="";
id="";
locationName="";
if(evt.attendanceCode){
attendance =AttendanceCodes.findOne({_id:evt.attendanceCode})
if(attendance){
color=attendance.color;
}
}
else if(evt.employeeAttendanceCode){
attendance =AttendanceCodesPortal.findOne({_id:evt.employeeAttendanceCode})
if(attendance){
color=attendance.color;
}
}
id=evt._id;
if(evt.locationId!==Session.get("locationId")){
color="#EEEEEE";
id="oloc";
location =Locations.findOne({_id:evt.locationId});
if(location){
locationName=location.name;
}
}
if(evt.name != null){
event = {id:id,title:evt.name,start:evt.start,end:evt.end,color:color,resourceId:evt.uid,locationName:locationName};
}else{
event = {id:id,title:" ",start:evt.start,end:evt.end,color:color,resourceId:evt.uid,locationName:locationName};
}
events.push(event);
});
allUsersCursor =Meteor.users.find({
$or:[
{"profile.showInScheduler":{$exists:false}},
{"profile.showInScheduler":true}
],
assignedTo:{$in:[Session.get("locationId")]},
'locations._id':Session.get("locationId"),
"profile.companyId":Session.get("companyId")
}).fetch();
if(calendar){
calendar.removeEvents();
calendar.addEventSource(events);
calendar.refetchResources();
//SetUp the actions context menu
setUpContextMenu();
// Initialize the external events
$('#external-events div.external-event').each(function() {
var eventObject = {
title: $.trim($(this).text()), // use the element's text as the event title
id:$(this).attr("id")
};
// store the Event Object in the DOM element so we can get to it later
$(this).data('eventObject', eventObject);
// make the event draggable using jQuery UI
$(this).draggable({
helper: 'clone',
revert: 'invalid',
appendTo: 'body'// original position after the drag
});
});
}
});
I can see a number of non-Meteoric patterns in there that you should optimize out:
Your this.autorun will rerun anytime anything changes in your Schedules collection as well as for various user object changes (not just to the logged-in users but other users as well). Your autorun rebuilds your client-side data from scratch every time, including numerous joins. You could use a local collection to cache the joined data and then only add/change those documents when the underlying persisted document changes. This can be done with an observeChanges pattern. Or you could denormalize your data model a bit to avoid all those joins.
You're modifying the DOM by attaching events to a number of selectors using jQuery instead of using Meteor event handlers. This is also happening in your autorun which means you're attaching the same events to the same DOM objects repeatedly.
You use many Session variables and you use them repeatedly. These can be slow since they use browser local storage. You should .get() such data into a local variable once only and then refer to the local variable from then on.
Make sure that you only include fields in your Schedules publication that your code actually refers to on the client. The rest are overhead.

how to waitOn data ready using iron-router and publish-composite

I have the following route :
this.route('groupPage', {
path: '/group/:_groupId',
waitOn: function(){
return Meteor.subscribe("groupPage", this.params._groupId);
},
data: function() {
var group = Groups.findOne({_id: this.params._groupId});
var members = Meteor.users.find({_id : {$in: group.memberIds}}); ******** ISSUE HERE******
return {
group: group,
members: members,
}; }});
and the following publication :
Meteor.publishComposite('groupPage', function(groupId, sortOrder, limit) {
return {
// return the group
find: function() {
if(this.userId){
var selector = {_id: groupId};
var options = {limit: 1};
return Groups.find(selector, options);
}
else{
return ;
}
},
children: [
{ // return the members
find: function(group) {
var selector = {_id: {$in: group.memberIds} };
return Meteor.users.find(selector);
}
}
]}}) ;
Now my issue is that : when the related page renders for the first there is no problems but when i actualize the group Page view the line : var members = Meteor.users.find({_id : {$in: group.memberIds}}); gives me the error : undefined object don't have memberIds property. i guess it's because the subscription is not yet ready when doing group.memberIds , isn't it ? Please a hint.
Thanks.
The data function doesn't wait for the subscription to be ready. Further more, subscriptions in the router are considered an anti-pattern for the most part, and should be done in the template: https://www.discovermeteor.com/blog/template-level-subscriptions/
I would pass to the template the groupId, and then get the group and members in the template, like so:
this.route('groupPage', {
path: '/group/:_groupId',
data: function() {
return {
_groupId: this.params._groupId,
}
}
});
and then in the template file:
Template.groupPage.onCreated(function(){
this.subscribe("groupPage", this.data._groupId);
})
Template.groupPage.helpers({
members(function(){
tempInst = Template.instance()
var group = Groups.findOne({_id: tempInst.data._groupId});
return Meteor.users.find({_id : {$in: group.memberIds}});
})
})
The general pattern of your route and publication are all solid. I suspect it's something simple such as:
There is no group with the _id you're using
You're not logged in when you load the route
Here's a version of your code that guards against the error. Note that the publication executes this.ready() instead of just returning if the user is not logged in.
this.route('groupPage', {
path: '/group/:_groupId',
waitOn: function(){
return Meteor.subscribe("groupPage", this.params._groupId);
},
data: function() {
var group = Groups.findOne({_id: this.params._groupId});
var members = group && Meteor.users.find({_id : {$in: group.memberIds}});
return { group: group, members: members };
}
});
Meteor.publishComposite('groupPage', function(groupId,sortOrder,limit) {
return {
find: function() {
if (this.userId) return Groups.find(groupId);
this.ready()
}
},
children: [
find: function(group) {
var selector = {_id: {$in: group.memberIds} };
return Meteor.users.find(selector);
}
]
});

Meteor.js : SearchSource + Publish composite

I'm currently creating a research engine for my app.
Until now, I used Publish composite + iron router : The user could had filters to search for some specific set of users.
Now, I want him to be able to look for some keywords too. For that I downloaded and tested the great SearchSource package.
The problem is that the SearchSource server side definition only seems to allow to return one cursor.
How could I combine the two logics ? Even if it's tricky, please, share.
Of course I could make an autorunned subscription where I look for every users loaded on the client and then subscribe to the additionnal documents, but it is not really the most performant and beautifull thing to do.
Some data :
Here is my current Publish Composite for filters :
Meteor.publishComposite("findTalkers", function(page, langs){
//console.log("Find Talkers");
//console.log("page : " + page);
//console.log("langs : " + langs);
if (langs.length)
{
return ({
find: function()
{
if (langs && langs.length)
{
var test = {$in: langs};
preSelectedUsers = [],
selector = {
_id: {$ne: this.userId},
"profile.completed": true,
"profile.firstName": {$exists: true},
"profile.languages.native": {$exists: false},
"profile.languages.lang": test
};
Counts.publish(this, "nbUsers", Meteor.users.find(selector, {
fields: {_id: 1}
}), {noReady: false, nonReactive: true});
if (page > 1)
{
preSelectedUsers = Meteor.users.find(selector, {
sort: {'profile.talkname': 1},
limit: 25,
skip: (25 * (page || 1)),
fields: {_id: 1}
}).fetch();
var i = -1;
while (preSelectedUsers[++i])
preSelectedUsers[i] = preSelectedUsers[i]._id;
}
if (page > 1)
selector._id = {$in: preSelectedUsers};
return Meteor.users.find(selector, {
fields: userFields,
sort: {'profile.talkname': 1},
limit: 25
});
}
},
children: [
{
// Finding user's profile picture if it is not url
find: function(user)
{
if (user && user.profile && user.profile.avatar.type != "url")
return Images.find({_id: user.profile.avatar.data}, {sort: {uploadedAt: -1}, limit: 1});
}
}
]
});
}
else
{
return ({
find: function()
{
return Meteor.users.find({_id: "flush"});
}
});
}
});
Here is my research with SearchSource :
Client :
var searchOptions = {
keepHistory: 1000 * 60 * 5,
localSearch: true
},
SearchSources = {
talkersSearch: new SearchSource('users', ['profile.talkname'], searchOptions)
};
Router.map(function(){
this.route('talkers/:page?',
{
template: "talkers",
onBeforeAction: function(pause){
(Meteor.user() && Meteor.user().profile.completed)
? this.next()
: this.render('/profile');
},
waitOn: function(){
var filters = MatesFilter.find().fetch(),
i = -1;
while (filters[++i])
filters[i] = filters[i].value;
if (filters.length)
{
return Meteor.subscribe("findTalkers", (this.params.page || 1), filters, function(){
Session.set('numberuser', Counts.get("nbUsers"));
});
}
return Meteor.subscribe('myself');
}
});
}
Template.talkers.helpers({
getPackages: function() {
return SearchSources.talkersSearch.getData({
transform: function(matchText, regExp) {
return matchText.replace(regExp, "<b>$&</b>")
},
sort: {isoScore: -1}
});
}
}
Template.talkers.events({
"keyup #header-search": _.throttle(function(e) {
Session.set("matesSearch", $(e.target).val().trim());
console.log("Searching for : " + text);
SearchSources.talkersSearch.search(Session.get("matesSearch"), {
page: (this.params.page || 1),
filters: filters
});
}, 200)
}
SERVER :
SearchSource.defineSource('users', function(searchText, options) {
var options = {sort: {"profile.talkname": -1}, limit: 25};
if(searchText)
{
var regExp = buildRegExp(searchText);
selector = { $or: [
{ "profile.talkname": regExp },
{ "profile.bio": regExp }
] };
return Meteor.users.find(selector, options).fetch();
}
return ;
});
All this Gives me two sources from which I can get users. I'd want to get a mean to merge the two ides (a composition of publication INSIDE the search, for example).
Thanks you.

Resources