ScriptDB auto numbering - google-apps-script

I have a database like this one:
var db = ScriptDb.getMyDb();
var ob = {
type: "employee",
employee_id: 1,
name: {
first: "Fatima",
initial: "S",
last: "Pauli"
},
address: {
street: "4076 Washington Avenue",
city: "Jackson", state: "MS", zip: "39201"
},
department_id: 52
};
var stored = db.save(ob);
Now I want the employee_id to have auto-increment without looping trough the entire existing database.
What is the best way to do this?

A solution is to store a current max value of employee_id in a separate ScriptDB object and use the LockService to provide atomicity, as shown here, during reading and incrementing operations.

Related

adding contact to existing group (people api migration)

Trying to get all this stuff migrated from the much simpler Contact API before it's switched before it's switched off in a few days. I'm able to add people now, but adding them to the group that is shared company wide isn't working.
// https://stackoverflow.com/questions/64095816/add-a-created-contact-to-a-group-by-people-api-using-google-apps-script
function createContactLead(lead) {
var contactResource = {
"names": [{
"displayNameLastFirst": lead["FirstName"] + " " + lead["LastName"],
"familyName": lead["LastName"],
"givenName" : lead["FirstName"]
}],
"phoneNumbers": [{
'value': lead["Phone"],
'type' : 'mobile',
}],
"emailAddresses": [{
'value': lead["Email"]
}],
"addresses": [{
"city": lead["city"],
"region": lead["state"]
}],
}
var peopleResource = People.People.createContact(contactResource);
var contactResourceName = peopleResource["resourceName"];
groupName = "Leads (Shared)";
var groups = People.ContactGroups.list()["contactGroups"];
var group = groups.find(group => group["name"] === groupName);
console.log(group);
var groupResourceName = group["resourceName"];
console.log("group resource name %s", groupResourceName);
var membersResource = {
"resourceNamesToAdd": [
contactResourceName
]
}
People.ContactGroups.Members.modify(membersResource, groupResourceName);
}
It definitely finds the group but trying to modify it results in " Invalid value at 'resource_names_to_add' (resource_names_to_add), Starting an object on a scalar field"
Execution log
12:34:02 PM Notice Execution started
12:34:03 PM Info { formattedName: 'Leads (Shared)',
groupType: 'USER_CONTACT_GROUP',
metadata: { updateTime: '2021-02-08T17:56:34.066Z' },
name: 'Leads (Shared)',
memberCount: 89,
etag: 'XadlO6et7QY=',
resourceName: 'contactGroups/27ee381f0e7d94e7' }
12:34:03 PM Info group resource name contactGroups/27ee381f0e7d94e7
12:34:03 PM Error
GoogleJsonResponseException: API call to people.contactGroups.members.modify failed with error: Invalid value at 'resource_names_to_add' (resource_names_to_add), Starting an object on a scalar field
createContactLead # test.gs:41
driver # test.gs:68
Thanks in advance!
Apparently I had a bug early on where I wasn't pulling out the "resourceName" from the people object. The code has since been updated in the question. Once that was fixed everything worked. I'll leave this up as it wasn't easy to get right.

How to read results returned by a query when using mysql in nodejs server? [duplicate]

I'm currently developing a desktop application with Node-webkit. During that process I need to get some data from a local MySQL-database.
The querying works fine, but I can't figure out how to access the results. I store all of them in an array that is then passed to a function. In the console they look like this:
RowDataPacket {user_id: 101, ActionsPerformed: 20}
RowDataPacket {user_id: 102, ActionsPerformed: 110}
RowDataPacket {user_id: 104, ActionsPerformed: 3}
And here is the query structure:
var ret = [];
conn.query(SQLquery, function(err, rows, fields) {
if (err)
alert("...");
else {
for (var i of rows)
ret.push(i);
}
doStuffwithTheResult(ret);
}
How do I retrieve this in the doStuffwithTheResult function? The values are more important, but if I could get the keys as well that would be great.
Turns out they are normal objects and you can access them through user_id.
RowDataPacket is actually the name of the constructor function that creates an object, it would look like this new RowDataPacket(user_id, ...). You can check by accessing its name [0].constructor.name
If the result is an array, you would have to use [0].user_id.
With Object.prototype approach, JSON.parse(JSON.stringify(rows)) returns object, extract values with Object.values()
let result = Object.values(JSON.parse(JSON.stringify(rows)));
Usage:
result.forEach((v) => console.log(v));
I also met the same problem recently, when I use waterline in express project for complex queries ,use the SQL statement to query.
this is my solution: first transform the return value(RowDataPacket object) into string, and then convert this string into the json object.
The following is code :
//select all user (查询全部用户)
find: function(req, res, next){
console.log("i am in user find list");
var sql="select * from tb_user";
req.models.tb_user.query(sql,function(err, results) {
console.log('>> results: ', results );
var string=JSON.stringify(results);
console.log('>> string: ', string );
var json = JSON.parse(string);
console.log('>> json: ', json);
console.log('>> user.name: ', json[0].name);
req.list = json;
next();
});
}
The following is console:
>> results: [ RowDataPacket {
user_id: '2fc48bd0-a62c-11e5-9a32-a31e4e4cd6a5',
name: 'wuwanyu',
psw: '123',
school: 'Northeastern university',
major: 'Communication engineering',
points: '10',
datems: '1450514441486',
createdAt: Sat Dec 19 2015 16:42:31 GMT+0800 (中国标准时间),
updatedAt: Sat Dec 19 2015 16:42:31 GMT+0800 (中国标准时间),
ID: 3,
phone: 2147483647 } ]
>> string: [{"user_id":"2fc48bd0-a62c-11e5-9a32-a31e4e4cd6a5","name":"wuwanyu","psw":"123","school":"Northeastern university","major":"Communication engineering","points":"10","datems":"1450514
441486","createdAt":"2015-12-19T08:42:31.000Z","updatedAt":"2015-12-19T08:42:31.000Z","ID":3,"phone":2147483647}]
>> json: [ { user_id: '2fc48bd0-a62c-11e5-9a32-a31e4e4cd6a5',
name: 'wuwanyu',
psw: '123',
school: 'Northeastern university',
major: 'Communication engineering',
points: '10',
datems: '1450514441486',
createdAt: '2015-12-19T08:42:31.000Z',
updatedAt: '2015-12-19T08:42:31.000Z',
ID: 3,
phone: 2147483647 } ]
>> user.name: wuwanyu
Hi try this 100% works:
results=JSON.parse(JSON.stringify(results))
doStuffwithTheResult(results);
You can copy all enumerable own properties of an object to a new one by Object.assign(target, ...sources):
trivial_object = Object.assign({}, non_trivial_object);
so in your scenario, it should be enough to change
ret.push(i);
to
ret.push(Object.assign({}, i));
you try the code which gives JSON without rowdatapacket:
var ret = [];
conn.query(SQLquery, function(err, rows, fields) {
if (err)
alert("...");
else {
ret = JSON.stringify(rows);
}
doStuffwithTheResult(ret);
}
going off of jan's answer of shallow-copying the object, another clean implementation using map function,
High level of what this solution does: iterate through all the rows and copy the rows as valid js objects.
// function will be used on every row returned by the query
const objectifyRawPacket = row => ({...row});
// iterate over all items and convert the raw packet row -> js object
const convertedResponse = results.map(objectifyRawPacket);
We leveraged the array map function: it will go over every item in the array, use the item as input to the function, and insert the output of the function into the array you're assigning.
more specifically on the objectifyRawPacket function: each time it's called its seeing the "{ RawDataPacket }" from the source array. These objects act a lot like normal objects - the "..." (spread) operator copies items from the array after the periods - essentially copying the items into the object it's being called in.
The parens around the spread operator on the function are necessary to implicitly return an object from an arrow function.
Solution
Just do: JSON.stringify(results)
I found an easy way
Object.prototype.parseSqlResult = function () {
return JSON.parse(JSON.stringify(this[0]))
}
At db layer do the parsing as
let users= await util.knex.raw('select * from user')
return users.parseSqlResult()
This will return elements as normal JSON array.
If anybody needs to retrive specific RowDataPacket object from multiple queries, here it is.
Before you start
Important: Ensure you enable multipleStatements in your mysql connection like so:
// Connection to MySQL
var db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '123',
database: 'TEST',
multipleStatements: true
});
Multiple Queries
Let's say we have multiple queries running:
// All Queries are here
const lastCheckedQuery = `
-- Query 1
SELECT * FROM table1
;
-- Query 2
SELECT * FROM table2;
`
;
// Run the query
db.query(lastCheckedQuery, (error, result) => {
if(error) {
// Show error
return res.status(500).send("Unexpected database error");
}
If we console.log(result) you'll get such output:
[
[
RowDataPacket {
id: 1,
ColumnFromTable1: 'a',
}
],
[
RowDataPacket {
id: 1,
ColumnFromTable2: 'b',
}
]
]
Both results show for both tables.
Here is where basic Javascript array's come in place https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
To get data from table1 and column named ColumnFromTable1 we do
result[0][0].ColumnFromTable1 // Notice the double [0]
which gives us result of a.
db.query('select * from login',(err, results, fields)=>{
if(err){
console.log('error in fetching data')
}
var string=JSON.stringify(results);
console.log(string);
var json = JSON.parse(string);
// to get one value here is the option
console.log(json[0].name);
})
conn.query(sql, (err,res,fields) => {
let rawData = res;
let dataNormalized = {...rawData[0]};
})
//Object Destructuring
This worked for me hope it helps you.
I think it is simplest way to copy object.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
Simpler way:
.then( resp=> {
let resultFromDb= Object.values(resp)[0]
console.log(resultFromDb)
}
In my example I received an object in response.
When I use Object.values I have the value of the property as a response, however it comes inside an array, using [0] access the first index of this array, now i have the value to use it where I need it.
I had this problem when trying to consume a value returned from a stored procedure.
console.log(result[0]);
would output "[ RowDataPacket { datetime: '2019-11-15 16:37:05' } ]".
I found that
console.log(results[0][0].datetime);
Gave me the value I wanted.
I had a similar problem and the solution was as follows:
const results = pool.query('sql sentence',[params]);
console.log((results[0])[0].name);
How to ACCESS what you get back from the database, this works for me:
async function getPageId(pageSlug){
let sql_update = 'SELECT id FROM pages WHERE pageSlug = ?';
let arrValues = [
pageSlug
];
let result = await mydb.query(sql_update, arrValues);
let r = JSON.parse(JSON.stringify(result));
if(r?.length){
return r[0].id;
}else{
return false;
}
}
I really don't see what is the big deal with this I mean look if a run my sp which is CALL ps_get_roles();.
Yes I get back an ugly ass response from DB and stuff. Which is this one:
[
[
RowDataPacket {
id: 1,
role: 'Admin',
created_at: '2019-12-19 16:03:46'
},
RowDataPacket {
id: 2,
role: 'Recruiter',
created_at: '2019-12-19 16:03:46'
},
RowDataPacket {
id: 3,
role: 'Regular',
created_at: '2019-12-19 16:03:46'
}
],
OkPacket {
fieldCount: 0,
affectedRows: 0,
insertId: 0,
serverStatus: 35,
warningCount: 0,
message: '',
protocol41: true,
changedRows: 0
}
]
it is an array that kind of look like this:
rows[0] = [
RowDataPacket {/* them table rows*/ },
RowDataPacket { },
RowDataPacket { }
];
rows[1] = OkPacket {
/* them props */
}
but if I do an http response to index [0] of rows at the client I get:
[
{"id":1,"role":"Admin","created_at":"2019-12-19 16:03:46"},
{"id":2,"role":"Recruiter","created_at":"2019-12-19 16:03:46"},
{"id":3,"role":"Regular","created_at":"2019-12-19 16:03:46"}
]
and I didnt have to do none of yow things
rows[0].map(row => {
return console.log("row: ", {...row});
});
the output gets some like this:
row: { id: 1, role: 'Admin', created_at: '2019-12-19 16:03:46' }
row: { id: 2, role: 'Recruiter', created_at: '2019-12-19 16:03:46' }
row: { id: 3, role: 'Regular', created_at: '2019-12-19 16:03:46' }
So you all is tripping for no reason. Or it also could be the fact that I'm running store procedures instead of regular querys, the response from query and sp is not the same.

if the json is duplicate, return the first element

I'm not sure if there is already a function for this, what I need is the following
a json in reactjs, and I need this to search if the cities are repeated and return only one result,
example if I have 5 berlin, only the first berlin returns
If I have 5 california, the first california returns
[
{id:1, city: "berlin"},
{id:2, city: "berlin"},
{id:3, city: "berlin"},
{id:5, city: "california"},
{id:6, city: "california"},
{id:7, city: "california"}
]
thanks for your help
Filter the results using a Set to check for duplicates:
const arr = [{"id":1,"city":"berlin"},{"id":2,"city":"berlin"},{"id":3,"city":"berlin"},{"id":5,"city":"california"},{"id":6,"city":"california"},{"id":7,"city":"california"}];
const result = arr.filter(function({ city }) {
return this.has(city) ? false : this.add(city);
}, new Set);
console.log(result);
Or reduce the array to a Map by country, and spread back to array:
const arr = [{"id":1,"city":"berlin"},{"id":2,"city":"berlin"},{"id":3,"city":"berlin"},{"id":5,"city":"california"},{"id":6,"city":"california"},{"id":7,"city":"california"}];
const result = [...arr.reduce((m, o) => m.has(o.city) ? m : m.set(o.city, o), new Map).values()];
console.log(result);

Sequelize mysql query where attribute is null

I have an address and I want to see if it's in the db already, if not, create a new one. I know I can use findOrCreate() here, but let's make it easy and just check why I can't even find the existing address.
var address = {
name: req.body.name,
address1: req.body.address1,
address2: req.body.address2,
zip: req.body.zip,
city: req.body.city,
country: req.body.country,
user_id: req.body.user_id
};
Address.find({where: address}).then(function(result){
console.log(result);
}).catch(function(err){
console.error(err);
});
The generated query asks for ... AND user_id = NULLwhich is wrong. It should ask ... AND user_id IS NULL. How can I let sequelize do it right for me? Thanks.
You should just use the JavaScript null primitive.
var address = {
name: req.body.name,
address1: req.body.address1,
address2: req.body.address2,
zip: req.body.zip,
city: req.body.city,
country: req.body.country,
user_id: req.body.user_id || null
};
This will generate the query
... user_id IS NULL
Additional information
model.findAll( { where: { some_column : undefined } } );
will generate
... WHERE `some_column` = NULL

Preprocessing Mongoose documents right after querying

Abstract
Hi, I have two models City and Country:
var Country = new Schema({
name: String,
population: String
});
var City = new Schema({
name: String,
timeZone: { type: Number, min: -12, max: 12 },
summerTime: Boolean,
country: { type: Schema.ObjectId, ref: 'Country'}
});
When I'm querying data from mongo using the following way:
function (request, response) {
var CityModel = mongoose.model('City', City);
CityModel.find().lean().populate('country').exec(function(err, docs){
response.send(JSON.stringify(docs));
});
}
But on client when I parse JSON, I get multiple instances of the same country:
var cities = $.get('/cities').then(function(citiesJson){
var cities = JSON.parse(citiesJson);
var washingtonIndex = ...;
var californiaIndex = ...;
var washington = cities[washingtonIndex];
var california = cities[californiaIndex];
washington.country.population = "300 000 000";
california.country.population = "500 000 000";
console.log([california.country.population, california.country.population]);
});
Resulting with two values ["300 000 000", "500 000 000"] in the console.
The problem
To prevent this kind of behaviour and preserve object references I'm doing JSON.decycle before object serialization:
...
response.send(JSON.stringify(JSON.decycle(docs)));
...
It works better than it should be. As a result I get the following JSON on the client:
// City:
{
name: "Bost",
deleted: "0",
country: {
$ref: "$[0]["city"]["country"]"
},
lastModified: "2013-08-06T23:44:11.000Z",
_id: {
_bsontype: "ObjectID",
id: "Rm1'"
},
approved: "1"
}
Notice the _id field which is getting serialized by reference so I don't get the actual ObjectId's string representation on the client, instead I'm getting some internal mongo representation of the id which is not usable on the client.
The question
1) Is there a way to setup per model or per schema preprocessing for all queried documents so that I convert ObjectId's into string
2) Probably there is some other more efficient way of dealing with this problem?
Thanks