Create item if not found and return old one if existing in dynamodb, in one call - amazon-dynamodb

In DynamoDb, is it possible to do a conditional put and return the old item if there already was a matching item?
I would like something like the following to create the row if the user does not already exist, and if it does exist, I want it to return the old item (with whatever name was there before). I.e., insert item if new, else just read existing item.
await documentClient.put({
TableName: 'table',
Item: {
userId: 'user0',
name: 'smith',
},
ConditionExpression: 'attribute_not_exists(userId)',
});
Is this possible to do in one call?

Sort of.
If you use UpdateItem it will create or update the item. It only updates the fields that have changed however, so if your fields have not changed, then no update will be made. It will also create the item if you do not have it.
Using Conditional Items for this will return an ErrorCode if the condition fails ConditionalCheckFailedException - which would require an additional operation.
(I don't use the javascript SDK that much, more in python, so I'm not sure this is the correct version/page but here is some documentation:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#updateItem-property )

Related

Firebase Cloud Firestore create entry or update if it already exists

I have an issue of knowing when to add or update an entry to a Firebase Firestore database.
Using doc_ref.set will add a document if it does not exist. It will also override all of a documents fields if it already exists and set is called.
Using doc_ref.update will update fields of a document if the document exists. If the document does not exist, nothing happens.
How do I add a new field to a document if the field does not currently exist, or update the field if it does exist? I could read the database and check if the field exists, and then use either set or update, but surely there is an easier way?
What you're looking for is the merge option in the set operation. From the documentation on setting a document:
var cityRef = db.collection('cities').doc('BJ');
var setWithMerge = cityRef.set({
capital: true
}, { merge: true });
If you're not sure whether the document exists, pass the option to merge the new data with any existing document to avoid overwriting entire documents.
I had similar problem, but I wanted to update array field using FieldValue.arrayUnion, FieldValue.arrayRemove. Therefore could not use set/merge.
To avoid checking if document exists before update (costs 1 read), I wrapped update statement with try/catch and check for error code 5 (NOT FOUND). In this case, set value instead of update.
Code example:
try {
await docRef.update({
arrayField: admin.firestore.FieldValue.arrayUnion(...dates),
});
} catch (error) {
if (error.code === 5) {
// Error: 5 NOT_FOUND: no entity to update:
await docRef.set({
arrayField: dates,
});
}
}

How to get the id/key of added firebase item directly in Ionic 2?

I'm trying to get the id/key of added item after adding processing
for example, this is my add function
add(){
let todayTime = moment().format('YYYY-MM-DD, HH:mm')
this.afd.list('/questions/').push({
uemail: this.userService.email,
title: this.variables.title,
text: this.variables.text,
type: this.variables.type
})
}
So how can I get the id directly (when item added successfully) ?
According to firebase doc
let key = this.afd.list('/questions/').push().key;
You get the key before pushing your data. It is useful when you need simultaneously add data in different locations using .update()

How to pass a fresh _id from a method insert into a subscription/publication?

I have an app where you can choose (or add if they don't exist!) a superhero/villain character from a certain universe on the first page; then outfit him with weapons, clothes, and gadgets on the second page (build).
I have this route defined:
Router.route('/build/:character', {
name: 'build'
waitOn: Meteor.subscribe('characters', {name: this.params.character})
//and a few other subscriptions and sessions as well for the items
//and stuff, but those don't matter here.
}
The link from the specific character, though, passes along a query as well:
<a href="{{pathFor 'build' query=this.universe}}">
So the final link could look something like this:
/build/Aquaman?DCComics
Now the page you are on will display a list of weapons and gadgets where you could also add other stuff if you so wish. Then you are supposed to drag the items you want to include onto your version of this hero.
Problem is, at this point the app doesn't know you even want to create your own hero. Maybe the user is just looking through them for fun. There's a button that the user has to click first to initialize the creating process, and that's when the actual _id is created, something like this:
Meteor.methods({
buildHero: function(heroCharacterName, heroUniverse) {
var heroToAdd = {}
heroToAdd['characterName'] = heroCharacterName
heroToAdd['universe'] = heroUniverse
heroToAdd['_createdAt'] = new Date()
CreatedHeroes.insert(heroToAdd, function() {
if (! error)
//Update the subscription somehow...
})
}
})
So, the _id that is created here in the new Collection must be passed along to a subscription somehow, because I don't want the user to see other personal heroes that have been created, only his own newly created one.
The solution I have in mind is adding the _id onto the URL in form of a hastag, and use this.params.hash in the subscription like so:
Router.route('/build/:character', {
name: 'build'
waitOn: [Meteor.subscribe('characters', {name: this.params.character}),
Meteor.subscribe('createdheroes', this.params.hash)]
}
First of all, is this a valid approach? If so, how do I accomplish it; how do I actually update the URL to include this hash?
If not, what would be a better approach?
I think you have to handle this logic in the data context or in a template helper and not in the way of subscribing/publishing.
If I was you I would besure that the newly created item is being published and subscribed by the client and modify your search query just that it only adds the newly created item.
I am not sure if I understand your question well but what I got, you will know the last _id which was used on your insert.
Instead of letting done this automatically by meteor, just use the meteor method to create / get that _id value >> see Meteor Documentation
var new_id = new Mongo.ObjectID()
col1.insert({ _id: new_id, ... });
col2.insert({ ..., ref_col1_id: new_id, ... });

How to open a collection item only on one client?

I have a meteor collection like this:
Cases = new Meteor.Collection('cases');
As well i have registered users (max 10). I now want to be able to "give" a single case to a registered user and be sure, that no other user is getting that specific case.
The User is working with the case (updating fields, deleting fields) and then sends it in some kind of archive after submitting the user should get a new case that is in the collection.
My thought was to have field called "locked" which initially is set to false and in the moment it is displayed at the user "locked" gets true and is not returned anymore:
return Cases.find({locked: false, done: false}, {limit: 1});
Any ideas how to do that in meteor?
Thanks
You just need to attach an owner field (or similar) to the case. That would allow you to do things like:
Only publish the case to the user who is also the owner using something like:
Meteor.publish('cases/unassigned', function() {
return Cases.find({owner: {$exists: false}});
});
Meteor.publish('cases/mine', function() {
return Cases.find({owner: this.userId});
});
Not allow a user to update or delete a case if it's not assigned to them:
Cases.allow({
update: function(userId, fieldNames, doc, modifier) {
return userId === doc.owner;
},
delete: function(userId, doc) {
return userId === doc.owner;
}
});
Obviously, these would need amending for stuff like super-users and you probably need some methods defined to allow users to take cases, but that's the general idea.
There are concurrency issues to deal with, to reliably allocate a case to only one person.
We need to solve two things:
1. Reliably assign the case to a user
2. Fetch the cases assigned to a user
Number 2. is easy, but depends on 1.
To solve 1., this should work:
var updated = Cases.update(
{_id: <case-to-assign>, version: "ab92c91"},
{assignedTo: Meteor.userId(), version: Meteor.Collection.ObjectID()._str});
if (updated) {
// Successfully assigned
} else {
// Failed to assign, probably because the record was changed first
}
Using this you can query for all of a users cases:
var cases = Cases.find({assignedTo: Meteor.userId()});
If 10 people try get a case at the same time, it should have a pre-set version field, and the MongoDB will only let the .update work once. As soon as the version field changes (due to an .update succeeding) the remaining updates will fail as the version field could no longer match.
Now that the allocation has taken place reliably, fetching is very simple.
As suggested by #Kyll, the filtering of cases should be done inside a Meteor publication.
It would also make sense to perform the case-assignment inside a Meteor method.
UPDATE:
#richsilv's solution is simpler than this one, and works fine.
This solution is useful if you need to know who won immediately, without making further requests to the server.

How do I delete or remove Session variables?

Meteor has a Session that provides a global object on the client that you can use to store an arbitrary set of key-value pairs. Use it to store things like the currently selected item in a list.
It supports Session.set, Session.get and Session.equals.
How do I delete a Session name, value pair? I can't find a Session.delete(name) ?
[note: this answer is for Meteor 0.6.6.2 through at least 1.1.0.2]
[edit: updated to also explain how to do this while not breaking reactivity. Thanks to #DeanRadcliffe, #AdnanY, #TomWijsman, and #MikeGraf !]
The data is stored inside Session.keys, which is simply an object, so you can manually delete keys:
Session.set('foo', 'bar')
delete Session.keys['foo']
console.log(Session.get('foo')) // will be `undefined`
To delete all the keys, you can simply assign an empty object to Session.keys:
Session.set('foo', 'bar')
Session.set('baz', 'ooka!')
Session.keys = {}
console.log(Session.get('foo')) // will be `undefined`
console.log(Session.get('baz')) // will be `undefined`
That's the simplest way. If you want to make sure that any reactive dependencies are processed correctly, make sure you also do something like what #dean-radcliffe suggests in the first comment. Use Session.set() to set keys to undefined first, then manually delete them. Like this:
// Reset one value
Session.set('foo', undefined)
delete Session.keys.foo
// Clear all keys
Object.keys(Session.keys).forEach(function(key){ Session.set(key, undefined); })
Session.keys = {}
There will still be some remnants of the thing in Session.keyDeps.foo and Session.keyValueDeps.foo, but that shouldn't get in the way.
Session.set('name', undefined) or Session.set('name', null) should work.
The disadvantage with using delete Session.keys['foo'] is that your template will not hot reload if the session key holds an array. For instance, if you are doing
Template.mytempl.helpers({
categories: function() {
return Session.get('srch-categories')
}
})
and in your template
{{#if categories}}
{{#each categories}}
{{this}}
{{/each}}
{{/if}}
And categories is an array, if you delete the session key, your template will continue to display the last value of categories.

Resources