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.
Related
Hello this is my first time. I have problem with like that I have 3000 items and I use framework like vue, element-ui and meteor. I pull all the items through a remote el-select that selects to add more remote select array object.I don't know why it slow performance and crush.
This is my picture
// Find item opts method
_getItemOpts(query, type) {
type = type || 'remote'
let exp = new RegExp(query)
let selector = {}
if (exp) {
selector = {
itemType: { $ne: 'Bundle' },
// , 'Sale'
activityType: { $in: ['Purchase'] },
status: 'Active',
$or: [
{ name: { $regex: exp, $options: 'i' } },
{ refNo: { $regex: exp, $options: 'i' } },
{ barcode: { $regex: exp, $options: 'i' } },
],
}
}
// Find item
findItems
.callPromise({ selector: selector })
.then(result => {
// console.log(result)
if (type == 'remote') {
this.itemOpts = result
}
this.loading = false
})
.catch(err => {
this.loading = false
Notify.error({ message: err })
})
},
Please help me.
Well no magick is here. U tryin to add to DOM 3000 of elements, so no wonder its crashes. Try to narrow results, for example show them only when user enters 3 letters
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.publishComposite('jobs', {
find: function() {
var user = null;
if (this.userId) {
user = Meteor.users.findOne(this.userId);
if ( user && user.profile && user.profile.isAdmin ) {
return Jobs.find({}, { sort: { createdAt: -1 }});
} else if(user && user._id) {
return Jobs.find({'createdBy': user._id});
}
} else {
return this.ready();
}
},
children: [
{
find: function(job) {
// Find post author. Even though we only want to return
// one record here, we use "find" instead of "findOne"
// since this function should return a cursor.
return Meteor.users.find(
{
_id: job.createdBy
},
{
fields: {
'profile': 1,
'createdAt': 1
}
}
);
}
}
]
});
This is the code I'm using from meteor-publishComposite package. I do not get the profile on my subscriptions for some reason. I can get the user.services to show up, but not the user.profile.
I set auth function:
UsersPagination = new Meteor.Pagination(Meteor.users, {
templateName: "usersPaginate",
itemTemplate: "userListItem",
router: "iron-router",
homeRoute: "/userlist/",
route: "/userlist/",
routerTemplate: "userListPage",
routerLayout: "indexLayout",
perPage: 50,
sort: {createdAt: -1},
auth: function(skip, sub){
//use alanning:roles
if(Roles.userIsInRole(sub.userId, ['admin'])) { // see all users
return Meteor.users.find({roles: 'user', user_status: {$in: [1,2,3,4,5]}}, {fields: {services: 0}});
} else if(Roles.userIsInRole(sub.userId, ['manager'])) { // see own users
return Meteor.users.find({manager_id: sub.userId, roles: 'user', user_status: {$in: [1,2,3,4,5]}}, {fields: {services: 0}});
} else {
console.log('whaa?');
return false ;
}
},
divWrapper: false,
fastRender: true,
availableSettings: {
filters: true,
settings: true
}
});
and if i set filter "user_status" in template:
Template.usersPaginate.events({
'change #user_status': function(event, template){ //select input
var status_id = parseInt(event.currentTarget.value) ;
var filter = {} ;
if(status_id !== 0) {
filter.user_status = status_id ;
}
//....
UsersPagination.set({
filters: filter
});
}
});
it's not working, template refresh, but filter not working. Displays all users.
Please tell me what I'm doing wrong.
Sorry my bad english :)
The combination auth and filter seems bugged or not clearly documented. Make your own pagination. You will spend less time in the end.
I've been modifying the example meteor app at http://meteor.com/examples/leaderboard. As you can see in the code bellow, I'm trying to update the score of players upon someone hitting the reset button. This updated fine on the client side but in my console I noticed the error "update failed: 500 -- Internal server error". Upon further inspection I saw that indeed, the server side database was not being updated. Any thoughts? (relevant code is in the reset function but I've posted the rest here just in case)
// Set up a collection to contain player information. On the server,
// it is backed by a MongoDB collection named "players."
Players = new Meteor.Collection("players");
var SORT_OPTIONS = {
name: {name: 1, score: -1},
score: {score: -1, name: 1}
}
var NAMES = [ "Ada Lovelace",
"Grace Hopper",
"Marie Curie",
"Carl Friedrich Gauss",
"Nikola Tesla",
"Claude Shannon" ];
function reset(options) {
if (options && options['seed'] === true) {
for (var i = 0; i < NAMES.length; i++) {
Players.insert({ name: NAMES[i], score: Math.floor(Math.random()*10)*5 });
}
}
if (options && options['restart'] === true) {
Players.update( {},
{ $set: { score: Math.floor(Math.random()*10)*5 } },
{multi: true});
}
}
if (Meteor.is_client) {
Template.leaderboard.players = function () {
var sort_by = SORT_OPTIONS[Session.get("sort_by")]
return Players.find({}, {sort: sort_by});
};
Template.leaderboard.selected_name = function () {
var player = Players.findOne(Session.get("selected_player"));
return player && player.name;
};
Template.player.selected = function () {
return Session.equals("selected_player", this._id) ? "selected" : '';
};
Template.leaderboard.events = {
'click input.inc': function () {
Players.update(Session.get("selected_player"), {$inc: {score: 5}});
},
'click input.sort': function () {
Session.get("sort_by") == "score" ? Session.set("sort_by", "name") : Session.set("sort_by", "score");
},
'click input.reset': function () {
reset({'restart': true});
}
};
Template.player.events = {
'click': function () {
Session.set("selected_player", this._id);
}
};
}
// On server startup, create some players if the database is empty.
if (Meteor.is_server) {
Meteor.startup(function () {
if (Players.find().count() === 0) {
reset({'seed': true});
}
});
}
This also happened to me, but checking the server log, the problem I had was that the $inc modifier requires a number for the argument for the update method, so I made sure it got it with
Number()
Time went by and it now works :) I guess it was some server issue on their demo deploy site.