DynamoDB putItem ConditionExpression "boolean" true - javascript

am trying to do a ConditionExpression in a DynamoDB put to check whether a stored boolean is true (in this example, whether the user is already verified don't run the put), i'm using the javascript DocumentClient SDK (thanks to #shimon-tolts), the code looks like:
var query = {
TableName: tableName,
Item: {
email: email,
verified: false,
verifyToken: token
},
ConditionExpression: 'attribute_exists(email) AND verified = :bool',
ExpressionAttributeValues: {
":bool":"false"
}
};
dynamodb.put(query, function(err, data){
if (err) return fn(err)
fn(null, data);
});
Which doesn't work, it fails the condition check no matter what the call.
Pretty much what I need (in pseudocode):
IF email already exists AND verified equals false
THEN allow PUT
IF email already exists AND verified equals true
THEN don't allow PUT
IF email does not exist
THEN allow PUT
Any ideas?

I suggest using DocumentClient as it works with javascript objects.
To do a condition expression you have to specify the ExpressionAttributeNames and ExpressionAttributeValues for example::
ConditionExpression: "#yr <> :yyyy and title <> :t",
ExpressionAttributeNames:{"#yr":"year"},
ExpressionAttributeValues:{
":yyyy":year,
":t":title
}
You can see more examples here and read more here

I finally got this to work after figuring out the right ExpressionAttributeValues:
dclient.scan(TableName='bix-workflow-images',
FilterExpression="wgs = :t or attribute_not_exists(wgs)",
ExpressionAttributeValues={':t':{'BOOL':True}})

I am stumbling about this because I have a similar problem and although this is a year old, for me it looks like your boolean shouldn't be a string:
ExpressionAttributeValues: {
":bool": "false"
}
Instead:
ExpressionAttributeValues: {
":bool": false
}
Have you tried it that way?

Related

Mongoose - Deleting documents is unresponsive

I'm trying to use Mongoose (MongoDB JS library) to create a basic database, but I can't figure out how to delete the documents / items, I'm not sure what the technical term for them is.
Everything seems to work fine, when I use Item.findById(result[i].id), it returns a valid id of the item, but when I use Item.findByIdAndDelete(result[i].id), the function doesn't seem to start at all.
This is a snippet the code that I have: (Sorry in advance for bad indentation)
const testSchema = new schema({
item: {
type: String,
required: true
},
detail: {
type: String,
required: true
},
quantity: {
type: String,
required: true
}
})
const Item = mongoose.model("testitems", testSchema)
Item.find()
.then((result) => {
for (i in result) {
Item.findByIdAndDelete(result[i].id), function(err, result) {
if (err) {
console.log(err)
}
else {
console.log("Deleted " + result)
}
}
}
mongoose.connection.close()
})
.catch((err) => {
console.log(err)
})
I'm not sure what I'm doing wrong, and I haven't been able to find anything on the internet.
Any help is appreciated, thanks.
_id is a special field on MongoDB documents that by default is the type ObjectId. Mongoose creates this field for you automatically. So a sample document in your testitems collection might look like:
{
_id: ObjectId("..."),
item: "xxx",
detail: "yyy",
quantity: "zzz"
}
However, you retrieve this value with id. The reason you get a value back even though the field is called _id is because Mongoose creates a virtual getter for id:
Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field cast to a string, or in the case of ObjectIds, its hexString. If you don't want an id getter added to your schema, you may disable it by passing this option at schema construction time.
The key takeaway is that when you get this value with id it is a string, not an ObjectId. Because the types don't match, MongoDB will not delete anything.
To make sure the values and types match, you should use result[i]._id.

How to get string value from query parameters

I'm trying to get the string value within a class-object variable called question5. I'm trying to access this variable like so in the front-end.
axios.get("http://localhost:3001/users/questionaire/?getquestion5=question5")
.then((res) => console.log(res.data))
Also, this is how it looks inside of the object in the js file.
const [answerslist, setAnswerslist] = useState(
{
question1: "",
question2: "",
question3: "",
question4: "",
question5: "",
question6: "",
question7: "",
question8: ""
}
)
However, when queried and log onto the console from the backend it literally just logs the string 'question5.' In other words, the string is literally just question5 not the text being added to it.
router.route('/questionaire').get((req, res) =>{
console.log(req.query.getquestion5)
User.find({email: req.query.getquestion5}, function (err, docs){
if(docs){
console.log("Email exist")
console.log(`${req.query.getquestion5}`);
console.log(docs)
}
else{
console.log(req.query.getquestion5)
console.log("Email doesnt exist")
}
}).clone().catch(function(err){console.log(err)})
})
Any reason why this might be the case?
If your intention is to send the answerlist.question5 state value in the query string getquestion5 parameter, try using Axios' params config option
params are the URL parameters to be sent with the request
Must be a plain object or a URLSearchParams object
NOTE: params that are null or undefined are not rendered in the URL.
axios.get("http://localhost:3001/users/questionaire", {
params: {
getquestion5: answerslist.question5
}
});
You'd then receive whatever value was in state at the time the request was made into the req.query.getquestion5 property.
Using params is preferable to interpolating strings directly into the URL since it will automatically URL-encode values that otherwise would not be valid. The equivalent safety measure would look like this...
axios.get(
`http://localhost:3001/users/questionaire?getquestion5=${encodeURIComponent(answerslist.question5)}`
);
which gets unmaintainable with more parameters.

DynamoDB search object with queries filter

{
"INFO": {
"email": "test#example.com",
"password": "123"
},
"PK": "3a95eab0-57de-4e15-90ea-004082e53384",
"SK": "user"
}
Above is my dataset in dynamoDB. I am building login api with expressjs with dynamodb. I am able to scan and update data with PK & SK keys but i want to query inside my INFO set.
I am trying like this:
var params = {
TableName: "table",
FilterExpression: "contains (INFO, :sendToVal)",
ExpressionAttributeValues: {
":sendToVal": {
email: "test#example.com",
password: "123",
},
},
};
But its returning:
{ error: 'Error retrieving Event' }
{ error: 'Event not found' }
Anyone help guide me, how can i retrive the set.
The DynamoDB documentation explains that the contains() function in an expression only works for strings or sets. This isn't completely accurate - it also works for lists. But in any case, it doesn't work for maps, which is what your INFO is, so the comparison doesn't match anything.
If you intended for INFO to be a list, not a map, please make it so. Otherwise, if you really intended for it to be a map, and you wanted to test whether { email: "test#example.com", password: "123" } is in that map, then what you really need to check is whether the email and password entries in this map is equal to the desired value. So the filter condition can be something like INFO.email = :email AND INFO.password = :password. Or something like this (I'm not sure I understannd what your intention was here).

Mongodb version 3+: how to insert new document only if it doesn't exist and get the result

I have tried dozens of solutions, and none of these worked and most of them are deprecated.
I have a collection with documents like this:
_id: 5d99ef3285c93711828cd15d
code: 1234
name: "Foo"
surname: "Bar"
address: "That street"
phone: 1234567
I would like to insert new document only if there isn't any document with the same code.
My last try was this:
const result = await db.collection('users').findOneAndUpdate(
{ code: user.code },
{
$setOnInsert: user,
},
{
upsert: true,
}
);
but I get E11000 duplicate key error collection...
updateOne() returns the same error; update() is deprecated...
So, how to add only new document and get the result (true if document has been added or false if it already exists)?
Thank you.
As far as my knowledge is,
with $set and $setOnInsert, we can not update/insert the same field.
i.e. $set and $setOnInsert should be mutually exclusive.
It works if the document is being updated, but throws an exception if document is being inserted.
In case of update, $setOnInsert will be ignored.
In case of insertion, both will be executed.
I think the solution would be,
use returnNewDocument and have one field in the schema isUpdated defaults to false.
Note:
make sure whenever you use "insert" operation on the collection, you don't add isUpdated which will be set to false then or set it to false.
form a query like
db.collection('users').findOneAndUpdate(
{ code: user.code },
{
$set: user, // which has user.isUpdated set to true
},
{
upsert: true,
returnNewDocument: false, // (by default it is false only)
}
);
With this logic,
So let's go step by step,
If the document doc1 is not present, it will be inserted, and mongo will return the response null. You will know, it is Inserted.
If the document doc2 is present(considering this logic is not applied on the previously inserted document doc2 before and isUpdated field is not present in doc2), it will execute $set so in returned cursor, this field not present i.e. undefined, so you know from this, it is updated.
let's say we fire the same query for doc1 again (which is present and we have applied our new logic), then there are two cases
a. it will be updated and in the cursor, we have isUpdated equal to false.
b. it will be updated and in the cursor, we have isUpdated equal to true.
In both case you know it is Updated
I think this approach should solve your problem.
Please share if this helps you, or you find any other solution.
UPDATE
ACTUALLY
You dont even need another field isUpdated, without this fiels this should work with the same logic.
i.e. If cursor is null then its inserted, if not null then its updated.
You can still run a query like this;
document = db.collection('users').findOne({code:userCode});
if(document == null){
//document doesn't exists so you can use insertOne
}
else{
// document exists, sou you can update
}
it won't be efficient but it'll do the work.
You can simply wrap the request with a try/catch block to catch the Error. Then return false when this exception occured.
You could use findOne to see if a user with that code exists first
const result = await db.collection('users')
.findOne({ code: user.code });
if( result ){
return res
.status(400)
.json({ errors: [{ msg: 'User already exists' }] });
}
//create
user = new User({
code = code,
name = name,
foo = foo
)}
//save
user.save();
res.json(user);
Try this one
db.collection("users").findOne({ code: user.code }, (err, data) => {
if (data) {
return res.send(false);
} else {
// a document
var user = new User({
code: code,
name: "Foo",
surname: "Bar",
address: "That street",
phone: 1234568
});
// save model to database
user.save(function(err, book) {
if (err) return console.error(err);
return res.send(true);
});
}
});
Users.findOneAndUpdate({code:user.code}, {upsert: true, new: true, setDefaultsOnInsert: true }, function(error, result) { if (error) return; // do something with the document }).
I think it would work. Let us know if you have any questions.

Mongoose: Incrementing my documents version number doesn't work, and I'm getting a Version Error when I try to save

When I try to save my document, I'm getting a VersionError: No matching document found error, similar to this SO question.
After reading this blog post, it seems that the problem is with the versioning of my document. That I'm messing with an array and so I need to update the version.
However, calling document.save() doesn't work for me. When I log out the document before and after the call to save(), document._v is the same thing.
I also tried doing document._v = document._v++ which also didn't work.
Code
exports.update = function(req, res) {
if (req.body._id) { delete req.body._id; }
User.findById(req.params.id, function(err, user) {
if (err) return handleError(res, err);
if (!user) return res.send(404);
var updated = _.extend(user, req.body); // doesn't increment the version number. causes problems with saving. see http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html
console.log('pre increment: ', updated);
updated.increment();
// updated._v = updated._v++;
console.log('post increment: ', updated);
updated.save(function(err) {
if (err) return handleError(res, err);
return res.json(200, user);
});
});
};
Output
pre increment: { _id: 5550baae1b571aafa52f070c,
provider: 'local',
name: 'Adam',
email: 'azerner3#gmail.com',
hashedPassword: '/vahOqXwCwKQKtcV3KBQeFge/YB0xtqOj+YDyck7gzyALA/IP7u7BfqQhlVHBQT26//XfBTkaOCK2bQXg65OzA==',
salt: 'MvzXW7D4xuyGQBJNeFRoUg==',
__v: 32,
drafts: [],
starredSkims: [],
skimsCreated: [ 5550cfdab8dcacd1a7892aa4 ],
role: 'user' }
post increment: { _id: 5550baae1b571aafa52f070c,
provider: 'local',
name: 'Adam',
email: 'azerner3#gmail.com',
hashedPassword: '/vahOqXwCwKQKtcV3KBQeFge/YB0xtqOj+YDyck7gzyALA/IP7u7BfqQhlVHBQT26//XfBTkaOCK2bQXg65OzA==',
salt: 'MvzXW7D4xuyGQBJNeFRoUg==',
__v: 32,
drafts: [],
starredSkims: [],
skimsCreated: [ 5550cfdab8dcacd1a7892aa4 ],
role: 'user' }
The issue here has to do with using __v and trying to update it manually. .increment does not actually perform an increment immediately, but it does set an internal flag for the model to handle incrementing. I can't find any documentation on .increment, so I assume it is probably for use internally. The problem stems from trying to combine .extend with an object that already has __v (there are two underscores by the way, not that document.__v++ affects the model internally anyway) in addition to using .increment.
When you use _.extend it copies the __v property directly onto the object which seems to cause problems because Mongoose cannot find the old version internally. I didn't dig deep enough to find why this is specifically, but you can get around it by also adding delete req.body.__v.
Rather than finding and saving as two steps, you can also use .findByIdAndUpdate. Note that this does not use __v or increment it internally. As the other answer and linked bug indicate, if you want to increment the version during an update you have to do so manually.
Versioning was implemented to mitigate the doc.save() by design (not Model.update etc). But if you want you can try the following instead:
{$set: {dummy: [2]}, $inc: { __v: 1 }}
However this was a confirmed-bug according to the link
Please validate your mongoose version from the milestone of the above issue.
Thanks :)

Categories