I've been looking at the firebase util project at https://firebase.github.io/firebase-util/. I can't figure out if this is possible.
Say I have a data set like this:
{ clients:
{ client1: { groups: { group1: true, group2: true }, name: 'client1' },
client2: { groups: { group3: true, group4: true }, name: 'client2' } },
groups:
{ group1: { name: 'group1' },
group2: { name: 'group2' },
group3: { name: 'group3' },
group4: { name: 'group4' },
group5: { name: 'group5' } } }
I want to retrieve "client1" and resolve all the groups associated with client1, giving me back a dataset like this.
{ client1: { groups: { group1: { name: 'group1' }, group2: { name: 'group2' } }, name: 'client1' }
Is there a way to do this with Firebase-util?
There is an example on the website that is similar, where it is joining an account to a profile and then mapping the style to another reference point.
var ref = Firebase.util.join(
new Firebase('INSTANCE/account'),
{
ref: new Firebase('INSTANCE/profile'),
keyMap: {
name: 'name',
nick: 'nick',
style: new Firebase('INSTANCE/styles')
}
}
);
But, I'm not seeing how to do it with what I'm looking for.
Something like:
FirebaseUtil.join(
{ ref: new Firebase('INSTANCE/clients/client1'), keyMap: {name: 'name', groups: new Firebase('INSTANCE/groups')} }
)
Thanks for any hints!
You cannot use a nested list of groups as the keymap for clients/$client/groups. It must be a string pointing to exactly one reference.
The solution would be to retrieve the list separately from the meta data for the user:
var fb = new Firebase(URL);
getClients(fb.child('clients'), function(userId, data) {
console.log(userId, data);
});
function getGroupsForClient(clientId, callback) {
var indexRef = fb.child('clients/'+clientId+'/groups');
var groupsRef = fb.child('groups');
var intersectionRef = new Firebase.util.intersection(indexRef, groupsRef);
intersectionRef.once('value', callback);
}
function getClients(callback) {
fb.child('clients').on('child_added', function(snap) {
var userId = snap.name();
var userData = snap.val();
getGroupsForClient(userId, function(snap) {
userData.groups = snap.val();
callback(userId, userData);
});
});
}
Related
I want to make a SimpleSchema for documents with the the following format:
{
...,
permissions: {
foo: {allow: ["user1", "user2"]},
bar: {allow: ["admin"]},
}
}
If foo and bar were well-known strings in the schema, I would just do this:
const PermissionsSchema = new SimpleSchema({
allow: {type: [String]},
});
new SimpleSchema({
...,
'permissions.foo': {
type: PermissionSchema,
},
'permissions.bar': {
type: PermissionSchema,
},
})
However, in this case, there can be arbitrary string keys, not just foo and bar. The values must always match PermissionsSchema. Is there a way to express this?
Custom validators to the rescue!
import { ValidationError } from 'mdg:validation-error';
function permissionsValidator(keyRegEx) {
if (!(keyRegEx instanceof RegExp)) {
throw new Error('must pass a regular expression');
}
return function() {
// https://github.com/aldeed/meteor-simple-schema#custom-validation
const value = this.value;
for (let key in value) {
if (value.hasOwnProperty(key)) {
if (!keyRegEx.test(key)) {
return 'regEx';
}
try {
PermissionSchema.validate(value[key]);
} catch (ex) {
if (ex instanceof ValidationError) {
return ex.error;
}
}
}
}
};
}
new SimpleSchema({
...,
permissions: {
type: Object,
custom: permissionsValidator(/^.*$/),
blackbox: true,
optional: true,
defaultValue: {},
},
});
The error messages that come out are rubbish, though. Improvements or better strategies still welcome.
I have a collection like this one:
//Groups Collection
{
_id:1,
members: [
{
memberId: 'A1B2',
content: [1,2,3]
},
{
memberId: 'C10B',
content: [4,5,6]
}
]
},
{
_id:2,
members: [
{
memberId: 'A1B2',
content: [7,8,9]
},
{
memberId: 'F804',
content: [10,11,12]
}
]
}
and another Collection like this one:
//Users Collection
{
_id: 'A1B2',
name: 'Newton'
},
{
_id: 'C10B',
name: 'Gauss'
},
{
_id: 'F804',
name: 'Leibniz'
}
And I need a publication with all ids of users in Groups array with _Id = 1. I tryed:
Meteor.publish('themembers',idGroup,function() {
return Users.find({_id:{$in:Groups.findOne(idGroup).members.map(function(e) {return e.memberId})}});
});
then I subscribe:
Template.problem.onCreated(function() {
Meteor.subscribe('themembers',1);
});
Now I can access the members in the helper:
Template.problem.helpers({
members: function() {
return Users.find();
}
});
and the helpers works right too.
But now, If I add a new member to the group, it not appears in the list, ... my subscriptions seems not to be reactive.
What I doing Wrong?
Typical issue with Meteor:
You should look at peerlibrary:reactive-publish
https://github.com/peerlibrary/meteor-reactive-publish
Provided I have following models:
module.exports = function (sequelize, DataTypes) {
var WorkingCalendar = sequelize.define('WorkingCalendar', {
date: DataTypes.DATEONLY,
isWorking: DataTypes.BOOLEAN,
}, {
indexes: [{
unique: true,
fields: ['PeriodId', 'date']
}]
}, {
classMethods: {
associate: function (models) {
WorkingCalendar.belongsTo(models.Period);
}
}
});
return WorkingCalendar;
};
module.exports = function(sequelize, DataTypes) {
var Period = sequelize.define('Period', {
name: DataTypes.STRING,
numberOfPeriods: DataTypes.INTEGER
}, {
classMethods: {
associate: function(models) {
Period.hasMany(models.WorkingCalendar);
}
}
});
return Period;
};
And then trying to get the Period through the WorkingCalendar as follows:
return models.WorkingCalendar
.findAll({
attributes: [
'PeriodId',
'date'
],
include: [
{ model: models.Period }
],
group: ['date', 'PeriodId']
});
I'm getting following error: Unhandled rejection Error: Period is not associated to WorkingCalendar!
Yet it does work the other way around.
My question:
Why can't I get the Period through the WorkingCalendar? And what do I have to do to make sure I can?
I have already tried putting the foreignKey attribute on the association as wel as the as binding but to no avail sadly. Any help would be very welcome!
So finally found it.
The indexes should be in the same object as classMethods
WRONG
module.exports = function (sequelize, DataTypes) {
var WorkingCalendar = sequelize.define('WorkingCalendar', {
date: DataTypes.DATEONLY,
isWorking: DataTypes.BOOLEAN,
}, {
indexes: [{
unique: true,
fields: ['PeriodId', 'date']
}]
}, {
classMethods: {
associate: function (models) {
WorkingCalendar.belongsTo(models.Period);
}
}
});
return WorkingCalendar;
};
RIGHT
module.exports = function (sequelize, DataTypes) {
var WorkingCalendar = sequelize.define('WorkingCalendar', {
date: DataTypes.DATEONLY,
isWorking: DataTypes.BOOLEAN,
}, {
indexes: [{
unique: true,
fields: ['PeriodId', 'date']
}],
classMethods: {
associate: function (models) {
WorkingCalendar.belongsTo(models.Period);
}
}
});
return WorkingCalendar;
};
I have an autocomplete on a text box which shows zipcode, City, State. Currently when I start typing zipcode [e.g. 55414] the autocomplete works and starts to show the relevant zip,city and State. But I can't figure out how to trigger autocomplete if I start typing a city name. I want both of these triggers on the textbox. I tried to add another rule in the rules array but it doesn't work. ZipCodes collection has _id, city, state fields. _id is zipcode.
Template.search.helpers({
settings : function () {
return {
position: "bottom",
limit: 20,
rules: [{
collection: ZipCodes,
field: "_id",
template: Template.userPill
}]
}
}
Thanks in advance
I think you are using meteor-autocomplete
In that case you can use selector option
Template.search.helpers({
settings : function () {
return {
position: "bottom",
limit: 20,
rules: [{
collection: ZipCodes,
field: "_id",
template: Template.userPill,
selector: function(match) {
var regex;
regex = new RegExp(match, 'i');
return {
$or: [
{
'_id': regex
}, {
'city': regex
}
]
};
},
}]
}
}
})
I know this answer is too late for you, but it can be helpful to somebody in the future. Just do this inside your server-side publish function.
Meteor.publish('ZipCodesPublication', function(selector, options) {
let limitTo = Math.abs(options.limit) > 50 ? 50 : options.limit,
defaultSelector = selector._id,
regEx = defaultSelector.$regex,
regExOptions = defaultSelector.$options,
customSeletor = {
$or: [
{
city: {
$regex: regEx,
$options: regExOptions
}
},
{
_id: {
$regex: regEx,
$options: regExOptions
}
}
]
};
Autocomplete.publishCursor(Clients.find(customSeletor), this);
this.ready();
});
And just do this on the client:
Template.search.helpers({
settings : function () {
return {
position: "bottom",
limit: 20,
rules: [{
collection: 'ZipCodes',
subscription: 'ZipCodesPublication',
field: "_id",
template: Template.userPill
}]
}
}
I am trying to find the best way to do CRUD operations on a document that has a embedded document. [1 Business --> Many Services]
I have an business object that looks like this:
{
_id: "ZChonAoaksZ7kCvca",
name: "ABC",
description: "random description",
services: [
{ _id: "ZChonAoaksZ7kCvcf", service:"123", bufferEnd: 5 },
{ _id: "ZChonAoaksZ7kCvcg", service: "345", bufferEnd: 5 },
{ _id: "ZChonAoaksZ7kCvch", service: "567", bufferEnd: 5 },
]
}
Selections: i query my collection using:
collectionname.find({'services._id': 'ZChonAoaksZ7kCvcf'}).fetch()
as expected it simply returns the entire document. How can i return only the single sub document not the business object or an entire array? Is there a way just to get one item from the parent document such as:
{ _id: "ZChonAoaksZ7kCvcf", service:"123", bufferEnd: 5 }
Thanks
The 3 CRUD operations to edit Arrays within a MongoDB object from Meteor (care Update is the tricky one) are as follows:
Add a new item to document array:
Business.update({ _id: Business.findOne()._id }, {
$addToSet: {
services: {
_id: Random.id(),
service: $('#new_service_name').val(),
bufferEnd: $('#new_service_description').val(),
}
}
);
Edit an existing item in document array
var bus = Business.findOne().services;
var indexToUpdate = _.indexOf(_.pluck(bus, '_id'), Session.get('Service_selectedId'));
var modifier = { $set: {} };
modifier.$set["services." + indexToUpdate + ".service"] = $('#service_name').val();
modifier.$set["services." + indexToUpdate + ".description"] = $('#service_description').val();
Business.update(Business.findOne()._id, modifier);
Delete Operation
Business.update({ _id: Business.findOne()._id }, {
$pull: { services: { _id: Session.get('Service_selectedId') } }
});
You can filter the result after it's fetched:
var wholeDoc = Documents.findOne({'services._id': 'ZChonAoaksZ7kCvcf'});
var service = _.find(wholeDoc.services, function(service) {
return service._id === 'ZChonAoaksZ7kCvcf',
});