Meteor 1.0: Upsert with Mongo Selector - meteor

I am trying to do an upsert on the server side with a custom field as the unique identifier instead of a mongo id (data is being pulled from a 3rd party api).
A simplified version of what I am trying to achieve:
var myItem = {
myUniqueID : 'abc123',
name: 'foo'
};
MyCollection.upsert(
{
"myUniqueID ": myItem.myUniqueID
},
{
"$set": myItem
}
);
I receive the following error:
Error: Meteor does not currently support objects other than ObjectID as ids

It appears that this was caused by me adding:
MyCollection._ensureIndex({myUniqueID : 1}, {unique: 1});
right after declaring the Mongo Collection... even when I commented this line out the damage had been done.. I had to rename the collection (essentially create a new collection) to get past it.

Related

Denormalize with normalizr

I am trying to denormalize some data from the redux state, but I can't get it to work. I have tried putting together the denormalize function in all ways I can think. It is a guessing game and nothing have worked so far.
First I normalize the product data and put it into the state.
The entities are being put in state.data.products, including the products.
state.data: {
products: {
products: {
9: {
id: 9
attributes: [ 10, 45, 23 ],
things: [23, 55, 77]
},
11 //etc
}
attributes: {
10: {
id: 10
},
45: //etc
},
things: { //etc
}
}
So there is one property "products" that includes all entities, and there is another "products" for the "products" entity.
The normalizing works without problem:
normalize(data, [productSchema]);
The "productSchema" is defined like this:
productSchema = new schema.Entity('products', {
attributes: [attributeSchema],
things: [thingsSchema],
//+ other stuff
});
Now I want to denormalize a product, for example 9.
I use "connect" in a view component to call this function to retrieve the data from state and denormalize it:
export const denormalizeProduct = (state, productId) => {
const entities = state.data.products;
return denormalize(
productId,
[productSchema],
entities
);
}
If I call denormalizeProductById(state, 9) , I only get 9 back.
(similar to this question , but I couldn't find a solution from it, and it mixes together two examples)
If I put the "productSchema" without the array:
return denormalize(
productId,
productSchema,
entities
);
I get the error: TypeError: entities[schemaKey] is undefined.
Everything I have tried results in either "9" or the above error.
Edit:
found the error - added it below
I found the error - one of my schemas was wrong: I had created new schema.Entity('shippingZones') but in the reducer I added it to the state with a different name:
shipping_zones: action.payload.shippingZones
So it was looking for "shippingZones" while there was only "shipping_zones" I guess. So I changed it to new schema.Entity('shipping_zones') and shipping_zones: action.payload.shipping_zones
It's a pity that the message TypeError: entities[schemaKey] is undefined couldn't have specified "shippingZones" , then I could have saved a few days time.
So this code worked in the end:
const entities = state.data.products;
return denormalize(
productId,
productSchema,
entities
);

How to update a pushed Fire Base object

Pushed firebase objects are hidden behind a unique key, making referencing a pushed object difficult unless your using childAdded. And a reference is needed if you want to use update on the object.
{
"ideaCount" : 0,
"ideas" : {
"-KNVuaB6ZaRCLmH0q8ic" : {
"idea" : {
"likes" : 0,
"name" : "test"
}
},
//unique key, every idea will have a new one of these
"-KNVucJEgZxz_N0P8bcv" : {
//want to reference this
"idea" : {
"likes" : 0,
"name" : "jkl"
}
}
}
Is it possible to update that "idea" object without explicitly putting that unique key in the reference?
Can I start the reference after the unique key? From my experience you need to start the reference at the root.
To be more specific, I would like to be able to change the "likes" number up and down on button press and update seemed to be the best way to do it.
To simply answer your question, no. You will need the push key to complete the reference to ideas/some-idea/likes. However, I am assuming your are listing these "ideas" so people can like them in which you will always have the push key available. For example:
JavaScript
var theIdeas= [];
var ref = firebase.database().ref("ideas");
ref.once("value").then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
// Push key
var key = childSnapshot.key;
// childData will be the actual contents of the child
var childData = childSnapshot.val();
});
theIdeas.push(childData);
}, function(error) {
});
AngularJS
var ref = firebase.database().ref("ideas");
var ideas = $firebaseArray(ref);
<!-- Push key is stored inside $id -->
<div ng-repeat"i in ideas"> {{i.$id}} </div>
Do whatever you wish when you receive that information. The point is that if you have grabbed an "idea" then you do know what the push key is and should be able to use it has part of your reference.
References:
Firebase | key

Is a variable for a field name allowed during update in Meteor?

I have been trying to update a collection i have.Problem is,i dont know the field name so am relying on some logic to come up with the field name.
For instance
Er.update({ _id: "BCwrQEdiTr9GMmKWW" }, {
$set: {
"x" : y
}
});
where x is the field name.
This is my code
var obj = {};
obj[x] = y;
Er.update({ _id: "BCwrQEdiTr9GMmKWW" }, {$set: {obj}});
I am getting this error
update failed: MongoError: The dotted field 'ersubjects.0.English' in
'obj.ersubjects.0.English' is not valid for storage.
English is a field under ersubjects so i want to update it this way ersubjects.0.English and it works on mongo.
Why is this not working in meteor?.
You can't store documents that have a dot in the key. See this answer for an explanation.
What you can do is use lodash's extremely handy _.set function to create your object with dynamic keys like this:
var obj = {};
var variableKey = 'ersubjects';
_.set(obj, [variableKey, 0, 'English], 'someValue');
Now you can safely store this object to Mongo.

Is there a way to update the URL in Flow Router without a refresh/redirect?

Is there a way to update a part of the URL reactively without using FlowRouter.go() while using React and react-layout?
I want to change the value in the document that is used to get the document from the DB. For example, if I have a route like ~/users/:username and update the username field in the document, I then have to user FlowRouter.go('profile', {data}) to direct the user to that new URL. The "old" route is gone.
Below is the working version I have, but there are two issues:
I have to use FlowRouter.go(), which is actually a full page refresh (and going back would be a 404).
I still get errors in the console because for a brief moment the reactive data for the component is actually wrong.
Relevant parts of the component are like this:
...
mixins: [ReactMeteorData],
getMeteorData() {
let data = {};
let users = Meteor.subscribe('user', {this.props.username});
if (user.ready())
data.user = user;
return data;
}
...
updateName(username) {
Users.update({_id:this.data.user._id}, {$set:{username}}, null, (e,r) => {
if (!e)
FlowRouter.go('profile', {username});
});
},
...
The route is like this:
FlowRouter.route('/users/:username', {
name: 'profile',
action(params) {
ReactLayout.render(Main, {content: <UserProfile {...params} />});
}
});
The errors I get in the console are:
Exception from Tracker recompute function:
and
TypeError: Cannot read property '_id' of undefined

$firebaseObject.$save() overwrites the whole node

I have a plunker where I have a simple update-Method to update a recorcd. But when I add or update a property to the record, the whole record will be overwritten.
The Data
{
"org1" : {
"description" : "too lazy",
"name" : "Apple",
"website" : "http://www.apple.com"
},
"org2" : {
"name" : "Google",
"test1" : "test123",
"website" : "http://www.google.com"
}
}
update()
this.update = function() {
//var obj = new Organization('org2');
var obj = $firebaseObject(new Firebase('https://thefactorymess.firebaseio-demo.com/organizations/org2'));
obj.test1 = 'test123';
obj.$save().then(function() {
console.log('org new', obj);
});
};
Here is the plunker.
Can someone explain me this behavior?
The AngularFire $save() method indeed overwrites the current data at that location. It is implemented by calling the set() method of Firebase's JavaScript SDK, which does precisely that.
Since you're calling $save() immediately after creating the reference, the data hasn't been loaded from the Firebase servers yet. So you're creating an object with a single properties test1. When you then $save() that object to the server, you end up with an object with a single property there too.
If you only want to update some properties, you can call update().
var ref = new Firebase('https://thefactorymess.firebaseio-demo.com/organizations/org2');
ref.update({ test1: 'test123' });
This doesn't use AngularFire. But since AngularFire is built on top of the regular Firebase JavaScript SDK, they interoperate without problems.
Alternatively you could use AngularFire's $loaded() promise to wait for the object to have loaded completely. But that would lead to both a lot more code and pulling down data.
My rule of thumb is:
Only use AngularFire for binding things to the $scope. For everything else, use the Firebase JavaScript SDK.

Resources