postgres add rows from array of objects - postgresql

So what I'm trying to do is take an array of objects that are similar to the rows stored in the db (minus the id since that's auto-generated). However, I can't seem to figure it out. I'm using node-postgres. It would go something like this
const fooObj:Omit<Food, fooId> = {
// foodId omitted
a: 'a',
b: 'b',
c: 'c'
}
const fooArr = [...] // array of above Foo objects
const {rows} = pool.query(format(
`
INSERT INTO "foo" ("a","b","c")
VALUES // insert properties into column from each obj in fooArr //
RETURNING *
`,fooArr
))

Related

Trigger to delete a field and add a prefix to other field

In my collection there is a document that can receive three fields, but I need to keep only two according to the values informed, for example I have fields A, B and C, depending on the value I do not need to record field B or C. I also need that A prefix is written in field A. I followed this documentation and created the function where I can read the fields, but I couldn't change or delete them. I used the onCreate event.
See my sample:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.testFields =
functions.firestore.document('documentos/{documentoId}/ocorrencias/{ocorrenciaId}').onCreate(async (snapshot, context) => {
const ocorrencia = snapshot.data();
//I can read the values
fieldA = ocorrencia.fieldA;
console.log('Field A: ', fieldA); //'Teste'
fieldB = ocorrencia.fieldB;
console.log('Field B: ', fieldB); //5
fieldC = ocorrencia.fieldC;
console.log('Field C: ', fieldC); //6
if(fieldB > FieldC){
//the C field does not need to be recorded
prefix = 'B';
}else{
//the B field does not need to be recorded
prefix = 'C';
}
//now I need to record the prefix next to FieldA
//my FieldA should look like this: 'CTeste'
});
You could do something like this:
if (fieldB > FieldC) {
//the C field does not need to be recorded
prefix = 'B';
ocorrencia.fieldC = null;
} else {
//the B field does not need to be recorded
prefix = 'C';
ocorrencia.fieldb = null;
}
//now I need to record the prefix next to FieldA
//my FieldA should look like this: 'CTeste'
ocorrencia.fieldA = prefix + fieldA;
functions.firestore.collection('ocorrencias').update(ocorrencia);
NOTE: The way your code is currently structured, it is performing 2 write calls to every record you create, one on the actual creation of the record, followed by the update call I suggested. This could generate some overhead on your system moving forward, or at least increase your number of write operations, which could be significant on billing, I would suggest you perform this check not in a cloud function, but in your front end.

OrientDB: filter by entire embedded field value

I'm to insert a record with embedded field in OrientDB and then to query for that record using filter:
insert into MyClass set embeddedField = {'#type': 'd', 'id': 1}
works well, and
select from MyClass
returns the record I've added. But when I add where with filter for embeddedField, I get no results:
select from MyClass where embdeddedField = {'#type': 'd', 'id': 1}
I thought that it could happen because Orient adds #version field into embedded document, so I tried to search with version:
select from MyClass where embdeddedField = {'#type': 'd', '#version': 0, 'id': 1}
But still got no results.
Question: Any idea how to filter embedded field by entire document? Without the need to filter explicitly by each field of embedded document:
select from MyClass
where embdeddedField.id = 1
and embdeddedField.field2 = val2
and embeddedField.field3 = val3
Because for several reasons I would like to pass the entire object as single query parameter:
select from MyClass where embdeddedField = ?
Thats because the provided Hash is transformed to an embedded class.
Perhaps take a look at the ActiveOrientWiki https://github.com/topofocus/active-orient/wiki/Relations
To explain:
Take a class base, there a document with two properties (schemaless)
and add existing documents to it
( 0..9 ).each do | b |
base_record= Base.create label: b, first_list: []
( 0..9 ).each {|c| base_record.first_list << FirstList.create( label: c ) }
end
INFO->CREATE VERTEX base CONTENT {"label":0,"first_list":[]}
INFO->CREATE VERTEX first_list CONTENT {"label":0}
INFO->update #136:0 set first_list = first_list || [#147:0] return after #this
then links are embedded and the record looks like
=> #<Base:0x00000000041a60e0 #metadata={:type=>"d", :class=>"base", :version=>11, :fieldTypes=>"first_list=z", :cluster=>129, :record=>1},
#attributes={:first_list=>["#149:1", "#150:1", "#151:1", "#152:1", "#145:2", "#146:2", "#147:2", "#148:2", "#149:2", "#150:2"], :label=>"1"}>
if you expand the list items, you can query for "#type"
If you add a document without rid, the same thing happens
( 0..9 ).each {|c| base_record.first_list << FirstList.new( label: c ) }
20.04.(12:53:59) INFO->update #130:2 set first_list = first_list || [{ #type: 'd' ,#class: 'first_list' ,label: 0 }] return after #this
INFO->CREATE VERTEX base CONTENT {"label":"c","first_list":[]}
'#130:2'.expand
=> #<Base:0x00000000043927a0 #metadata={:type=>"d", :class=>"base", :version=>11, :fieldTypes=>"first_list=z", :cluster=>130, :record=>2},
#attributes={:first_list=>["#151:12", "#152:12", "#145:13", "#146:13", "#147:13", "#148:13", "#149:13", "#150:13", "#151:13", "#152:13"], :label=>"c"}>
If you omit the class-name, only the scope of the rid's changes
INFO-> update #132:2 set first_list = first_list || { #type: 'd' ,label: 0 } return after #this
=> [#<Base:0x0000000003a26fb8 #metadata={:type=>"d", :class=>"base", :version=>3, :fieldTypes=>"first_list=z", :cluster=>132, :record=>2},
#attributes={:first_list=>["#3:0"], :label=>"d"}>]
In any case, a query for #type failes

{pg-promise} postgres - how to convert type int[] to be compatible with POINT()

So I'm using pg-promise to insert into a type POINT column. But it's giving me the following error:
function point(integer[]) does not exist
I'm passing the values as an array. What should I change to make it work?
Some code (not sure if useful):
simplified_query = `$${counter++}:name = POINT($${counter++})`
fields =
[
"geolocation",
[10, 10]
]
As per Custom Type Formatting, if your field is ['geolocation', [10, 10]], with the first value being the column name, you can use the following function:
function asPoint(field) {
return {
rawType: true,
toPostgres: () => pgp.as.format('$1:name = POINT($2:csv)', field)
};
}
Then you can use asPoint(field) as a query-formatting parameter:
const field = ['geolocation', [10, 10]];
db.any('SELECT * FROM table WHERE $1', [asPoint(field)])
//=> SELECT * FROM table WHERE "geolocation" = POINT(10, 10)
Alternatively, your field can be a custom-type class that implements Custom Type Formatting either explicitly or via the prototype, in which case it can be used as a formatting parameter directly.

Bulk Operations - Adding multiple rows to sheet

I'm attempting to write data from a data table to a sheet via the smartsheet API (using c# SDK). I have looked at the documentation and I see that it supports bulk operations but I'm struggling with finding an example for that functionality.
I've attempted to do a work around and just loop through each record from my source and post that data.
//Get column properties (column Id ) for existing smartsheet and add them to List for AddRows parameter
//Compare to existing Column names in Data table for capture of related column id
var columnArray = getSheet.Columns;
foreach (var column in columnArray)
{
foreach (DataColumn columnPdiExtract in pdiExtractDataTable.Columns)
{
//Console.WriteLine(columnPdiExtract.ColumnName);
if(column.Title == columnPdiExtract.ColumnName)
{
long columnIdValue = column.Id ?? 0;
//addColumnArrayIdList.Add(columnIdValue);
addColumnArrayIdList.Add(new KeyValuePair<string, long>(column.Title,columnIdValue));
}
}
}
foreach(var columnTitleIdPair in addColumnArrayIdList)
{
Console.WriteLine(columnTitleIdPair.Key);
var results = from row in pdiExtractDataTable.AsEnumerable() select row.Field<Double?>(columnTitleIdPair.Key);
foreach (var record in results)
{
Cell[] cells = new Cell[]
{
new Cell
{
ColumnId = columnTitleIdPair.Value,
Value = record
}
};
cellRecords = cells.ToList();
cellRecordsInsert.Add(cellRecords);
}
Row rows = new Row
{
ToTop = true,
Cells = cellRecords
};
IList<Row> newRows = smartsheet.SheetResources.RowResources.AddRows(sheetId, new Row[] { rows });
}
I expected to generate a value for each cell, append that to the list and then post it through the Row Object. However, my loop is appending the column values as such: A1: 1, B2: 2, C3: 3 instead of A1: 1, B1: 2, C3: 3
The preference would be to use bulk operations, but without an example I'm a bit at a loss. However, the loop isn't working out either so if anyone has any suggestions I would be very grateful!
Thank you,
Channing
Have you seen the Smartsheet C# sample read / write sheet? That may be a useful reference. It contains an example use of bulk operations that updates multiple rows with a single call.
Taylor,
Thank you for your help. You lead me in the right direction and I figured my way through a solution.
I grouped by my column value list and built records for the final bulk operation. I used a For loop but the elements in each grouping of columns is cleaned and assigned a 0 prior to this method so that they retain the same count of values per grouping.
// Pair column and cell values for row building - match
// Data source column title names with Smartsheet column title names
List<Cell> pairedColumnCells = new List<Cell>();
//Accumulate cells
List<Cell> cellsToImport = new List<Cell>();
//Accumulate rows for additions here
List<Row> rowsToInsert = new List<Row>();
var groupByCells = PairDataSourceAndSmartsheetColumnToGenerateCells(
sheet,
dataSourceDataTable).GroupBy(
c => c.ColumnId,
c => c.Value,
(key, g) => new {
ColumnId = key, Value = g.ToList<object>()
});
var countGroupOfCells = groupByCells.FirstOrDefault().Value.Count();
for (int i = 0; i <= countGroupOfCells - 1; i++)
{
foreach (var groupOfCells in groupByCells)
{
var cellListEelement = groupOfCells.Value.ElementAt(i);
var cellToAdd = new Cell
{
ColumnId = groupOfCells.ColumnId,
Value = cellListEelement
};
cellsToImport.Add(cellToAdd);
}
Row rows = new Row
{
ToTop = true,
Cells = cellsToImport
};
rowsToInsert.Add(rows);
cellsToImport = new List<Cell>();
}
return rowsToInsert;

pg-promise update where in custom array

How can the following postgresql query be written using the npm pg-promise package?
update schedule
set student_id = 'a1ef71bc6d02124977d4'
where teacher_id = '6b33092f503a3ddcc34' and (start_day_of_week, start_time) in (VALUES ('M', (cast('17:00:00' as time))), ('T', (cast('19:00:00' as time))));
I didn't see anything in the formatter namespace that can help accomplish this. https://vitaly-t.github.io/pg-promise/formatting.html
I cannot inject the 'cast' piece into the '17:00:00' value without it being considered part of the time string itself.
The first piece of the query is easy. It's the part after VALUES that i can't figure out.
First piece:
var query = `update schedule
set student_id = $1
where teacher_id = $2 and (start_day_of_week, start_time) in (VALUES $3)`;
var inserts = [studentId, teacherId, values];
I'm using this messiness right now for $3 (not working yet), but it completely bypasses all escaping/security built into pg-promise:
const buildPreparedParams = function(arr, colNames){
let newArr = [];
let rowNumber = 0
arr.forEach((row) => {
const rowVal = (rowNumber > 0 ? ', ' : '') +
`('${row.startDayOfWeek}', (cast('${row.startTime}' as time)))`;
newArr.push(rowVal);
});
return newArr;
};
The structure I am trying to convert into this sql query is:
[{
"startTime":"17:00:00",
"startDayOfWeek":"U"
},
{
"startTime":"16:00:00",
"startDayOfWeek":"T"
}]
Use CSV Filter for the last part: IN (VALUES $3:csv).
And to make each item in the array format itself correctly, apply Custom Type Formatting:
const data = [{
startTime: '17:00:00',
startDayOfWeek: 'U'
},
{
startTime: '16:00:00',
startDayOfWeek: 'T'
}];
const values = data.map(d => ({
toPostgres: () => pgp.as.format('(${startDayOfWeek},(cast(${startTime} as time))', d),
rawType: true
}));
Now passing in values for $3:csv will format your values correctly:
('U',(cast('17:00:00' as time)),('T',(cast('16:00:00' as time))