Dynamoose/DynamoDB update saving empty array as null - javascript

I'm using the Node.js package Dynamoose to handle DynamoDB requests in my web application.
Problem is when I try to update the item whenever the JSON object includes an empty array Dynamoose seems to set that to null. Or it could be DynamoDB for all I know.
Below is part of my schema that I'm using for this table.
var NoteSchema = new dynamoose.Schema({
_id: String,
details: Array
});
In the code below the variable body is set to {details: []}. I have confirmed this by running console.log(body);.
Note.update({
_id: searchid
}, body, function(err, note) {
if (err) {
console.log(err);
} else {
console.log(note);
}
});
Problem is inside that callback function when running console.log(note); details doesn't even show up at all. So it's null or undefined. In the Amazon Web Services again details doesn't exist at all for that entry.
What is so strange is when creating a new Note, setting details = [], and saving that, details is an empty array and works perfectly. So to me it seems like a specific problem with updating the record and setting that property to an empty array, since creating a record and setting that property to an empty array works perfectly.
How can I update the record and set details to an empty array?

Figured this out. Submitted this issue to the GitHub repo. Basically the following line (lib/Model.js about line 396) of code checks to see if the value is an array with length = 0. If so it will delete that JSON key before sending it to AWS.
if(val === null || val === undefined || val === '' || (Array.isArray(val) && val.length === 0)) {
I submitted a pull request using my fork. In that pull request I made the change to allow an optional JSON object parameter of options to be passed into the update function. This is the 3rd parameter of the update function and right before the callback function. If there is a key called emptyarrayallowed and that value is set to true then you that line above will get changed into the following.
if(val === null || val === undefined || val === '') {
For example in my example above changing the update function to the following will work (as long as you are using my fork or the pull request has been approved).
Note.update({_id: searchid}, body, {"emptyarrayallowed": true}, function(err, note) {
if (err) {
console.log(err);
} else {
console.log(note);
}
});
Let me know if you have any questions regarding this solution.

Small update on Charlie's answer. The functionality was merged as mentioned by Charlie. However, looks like the property name was renamed to allowEmptyArray from emptyarrayallowed.

Related

How to create a conditional based on a response object, for which the Key values are dependent and avoiding object.key() is Undefined

I'm subscribing to a method in Angular using typescript, which is a simple post Call that returns an entire HTTP Response. I'm wanting to create a simple conditional that checks to see if a certain key exists in the HTTP response. The conditional will work with an object that contains the key, but for an object that doesn't contain the key it throws an undefined.
Is there a way to omit the error or exception thrown and to proceed to the else portion
The keys of the object body are added if the user includes a number, without that it doesn't exist with the PhoneNumber key value
what I've tried / flipped the if and else
!= undefined (vice versa)
== null (vice versa)
this.postMyName(userInfo).subscribe(
(result:Response) => {
if (result['body']['user'].hasOwnProperty(['PhoneNumber'])) {
console.log("User has included a phoneNumber");
} else {
console.log("User has Not included a phoneNumber");
}
},error ...)
You can also try this solution maybe it will work
this.postMyName(userInfo).subscribe(
(result:Response) => {
if ('PhoneNumber' in result['body']['user'])) {
console.log("User has included a phoneNumber");
} else {
console.log("User has Not included a phoneNumber");
}
},error ...)

Is there an elegant way to check if the POST request body matches the expected parameters at the endpoint? [duplicate]

This question already has answers here:
Detect whether object implement interface in TypeScript dynamically
(2 answers)
Closed 2 years ago.
I implemented a simple server in NodeJS using TypeScript and ExpressJS. At present, in each endpoint, I check if the incoming request body matches the expected parameters in the following way: -
express.Router().post('/add', (req: Request, res: Response) => {
if (!req.body.operand1 || typeof req.body.operand1 !== 'number' ||
!req.body.operand2 || typeof req.body.operand2 !== 'number') {
res.send('Request body is invalid');
return;
}
const parameters = req.body;
res.send(parameters.operand1 + parameters.operand2);
}
But this would get tedious if there are many expected parameters in a given endpoint. What is the best way to achieve this?
EDIT
I edited this question because, before I had included some interfaces and the question looked similar to Detect whether object implement interface in TypeScript dynamically, which (understandably) confused people, while the scenario was different from that. There, the asker wanted to know if an object implements an interface. Here, I want to see if the body in a post request is the expected one.
So here is my example and what I had in mind. Its a very basic example and goes under the assumption that everything you define in your rules MUST be present in the body:
const AddEndpointRules = {
operand1: 'number',
operand2: 'number'
};
express.Router().post('/add', (req: Request, res: Response) => {
// --- Changed
if (!matches(req.body, AddEndpointRules)) {
res.send('Request body is invalid');
return;
}
// ---
const parameters: AddEndpointParameters = req.body;
res.send(parameters.operand1 + parameters.operand2);
}
function matches(body, rules) {
// All keys of rules must be present, so we use that to check our body
for (let attribute in rules) {
// The type of value must be same as our rule
// Not present would mean 'undefined'
if (typeof body[attribute] !== rules[attribute]) {
// We invalidate body as soon as we find our first invalid attribute
return false;
}
}
// All checked out, request body is OK
return true;
}
You can also place the matches function in a class of its own and require/import it above your request and use it like for example: Validator.validate() (rename it to validate). That way you can also extend the logic some more and check for length and/or check deeper in the object.
What you are looking for is called a schema. It's a way to define what a json object should look like or be formatted as and then use that to compare against the body that is being sent to the API. I would look into this.
https://www.npmjs.com/package/jsonschema

How can I check if a data search was successful in Firebase

I am checking if a combination of child values exists in my DB.
This works but can't get the proper message to be displayed in the console.
the correct console message is displayed in both else statements. The one that is not being displayed properly is the console.log('yes').
Or actually it is displayed but always followed by aconsole.log('no') and I have no idea why
There is however a match found because the correct data is shown in console.log(details); console.log(snapshot.key);
FBSearch: function(){
var T1 = this
var T2 = this
var T3 = this
var ref = firebase.database().ref("flights");
ref
.orderByChild('date')
.equalTo(T2.flight.dat)
.on('child_added', function(snapshot) {
var details = snapshot.val();
if (details.flight == T1.flight.flt) {
if(details.origin == T3.flight.org){
console.log(details);
console.log(snapshot.key);
console.log('yes')
} else {
console.log('no')
}
} else {
console.log('no')
}
})
}
The desired outcome is a success message when a match is found
Not sure if this is the answer to your question, but something you might want to consider...
Right now you're reading all flights of the specific date, in an effort to check if one of them exists. This is suboptimal, and can waste lot of your users' bandwidth. Ideally a yes/no question like that should require you to pass in the criteria, and get a minimal yes/no type answer.
While the Firebase Realtime Database doesn't support exists() queries, and can't query on multiple conditions, you can make significant improvements by adding a special property to your data to support this query.
As far as I can see, you're filtering on three properties: date, flight, and origin. I recommend adding an extra property to all flights that combines the values of these properties into a single value. Let's call this property "origin_date_flight".
With that property in place you can run the following query to check for the existence of a certain combination:
ref
.orderByChild('origin_date_flight')
.equalTo(`${T3.flight.org}_${T2.flight.date}_${T1.flight.flt}`)
.limitToFirst(1)
.once('value', function(snapshot) {
if (snapshot.exists()) {
console.log('yes')
} else {
console.log('no')
}
})
The main changes here:
The query uses a composite property as a sort-of index, which allows it to query for the specific combination you're looking for.
The query uses limitToFirst(1). Since you're only looking to see if a value exists, there's no need to retrieve multiple results (if those exist).
The query uses once("value", which allows it to check for both existence and non-existence in a single callback. The child_added event only fired if a match exists, so can't be used to detect non-existence.
And as a bonus, this new property also allow you to for example get all flights out of Amsterdam today:
ref
.orderByChild('origin_date_flight')
.startAt(`AMS_20190728_${T2.flight.date}_`)
.endAt(`AMS_20190728_${T2.flight.date}~`)
.once('value', function(snapshot) {
snapshot.forEach(function(flightSnapshot) {
console.log(flightSnapshot.key+": "+flightSnapshot.child("flt").getValue());
})
})
Also see:
Query based on multiple where clauses in Firebase

Check if PouchDB database is empty

I am working on an app which stores data locally using PouchDB. It displays a list of items and I would like to be able to check if the database is empty so that in the case that it is, I can append a line 'Your list is empty' to the DOM. Is there a way to do this?
There are several ways to do this. You could query and check for an empty result list. You can also use db.info().
db.info().then(function (result) {
if(result.doc_count === 0) {
console.log('It's empty!');
}
});

Add a new field to a document mongodb

I am very new to mongodb and have a basic question that I am having trouble with. How do I get the ID field of a document that has already been created? I need the ID so i can update/add a new field to the document.
//newProfile is an object, one string it holds is called school
if(Schools.find({name: newProfile.school}).fetch().length != 1){
var school = {
name: newProfile.school
}
Meteor.call('newSchool', school);
//Method 1 (doesn't work)
var schoolDoc = Schools.findOne({name: newProfile.school});
Schools.update({_id: schoolDoc._id}, {$set: {enrolledStudents: Meteor.user()}});
//Method 2?
//Schools.update(_id: <what goes here?>, {$push: {enrolledStudents: Meteor.user()}});
}
else {
//Schools.update... <add users to an existing school>
}
I create a new school document if the listed school does not already exist. Schools need to hold an array/list of students (this is where i am having trouble). How do I add students to a NEW field (called enrolledStudents)?
Thanks!
I'm having some trouble understanding exactly what you're trying to do. Here's my analysis and understanding so far with a couple pointers thrown in:
if(Schools.find({name: newProfile.school}).fetch().length != 1){
this would be more efficient
if(Schools.find({name: new Profile.school}).count() != 1) {
Meteor.call('newSchool', school);
Not sure what you're doing here, unless you this will run asynchronously, meaning by the time the rest of this block of code has executed, chances are this Meteor.call() function has not completed on the server side.
//Method 1 (doesn't work)
var schoolDoc = Schools.findOne({name: newProfile.school});
Schools.update({_id: schoolDoc._id}, {$set: {enrolledStudents: Meteor.user()}});
Judging by the if statement at the top of your code, there is more than one school with this name in the database. So I'm unsure if the schoolDoc variable is the record you're after.
I believe you are having trouble because of the asynchronous nature of Meteor.call on the client.
Try doing something like this:
// include on both server and client
Meteor.methods({
newSchool: function (school) {
var newSchoolId,
currentUser = Meteor.user();
if (!currentUser) throw new Meteor.Error(403, 'Access denied');
// add some check here using the Meteor check/match function to ensure 'school'
// contains proper data
try {
school.enrolledStudents = [currentUser._id];
newSchoolId = Schools.insert(school);
return newSchoolId;
} catch (ex) {
// handle appropriately
}
}
});
// on client
var schoolExists = false;
if (Schools.findOne({name: newProfile.school})) {
schoolExists = true;
}
if (schoolExists) {
var school = {
name: newProfile.school
};
Meteor.call('newSchool', school, function (err, result) {
if (err) {
alert('An error occurred...');
} else {
// result is now the _id of the newly inserted record
}
})
} else {
}
Including the method on both the client and the server allows Meteor to do latency compensation and 'simulate' the insert immediately on the client without waiting for the server round-trip. But you could also just keep the method on the server-side.
You should do the enrolledStudents part on the server to prevent malicious users from messing with your data. Also, you probably don't want to actually be storing the entire user object in the enrolledStudents array, just the user _id.
For what you're trying to do, there is no need to get the _id. When you use update, just switch out the {_id: schoolDoc._id} with your query. Looks like using {name: newProfile.school} will work, assuming that the rest of your code does what you want it to do.
While that would work with the normal Mongo driver, I see that Meteor does not allow your update query to be anything but _id: Meteor throws throwIfSelectorIsNotId exception
First, make sure that you're pulling the right document, and you can try something like this:
var school_id = Schools.findOne({name: newProfile.school})._id;
Schools.update({_id: school_id}, { $push: { enrolledStudents: Meteor.user()}});
If that doesn't work, you'll have to do a little debugging to see what in particular about it isn't working.

Categories

Resources