Get nested object in structure in gorm - postgresql

I have two structures:
type GoogleAccount struct {
Id uint64
Token string
}
It represent my custom PostgreSQL object type (i created myself):
CREATE TYPE GOOGLE_ACCOUNT AS
(
id NUMERIC,
token TEXT
);
And next structure is table in DB:
type Client struct {
IdClient uint64 `gorm:"primary_key"`
Name string
PhotoUrl string
ApprovalNumber uint16
Phone string
Password string
HoursOfNotice int8
Google GoogleAccount
}
And my custom object nested in type Client and named as google. I've tried to read data by the next way:
var users model.Client
db.First(&users)
But unfortunately I can't read field google (have a default value). I don't want to create separate table with google_account, or make this structure as separated fields in client table or packed it as json (created separate entity, because this structure used not only in this table and I'm searching new ways, that get the same result, but more gracefully). The task is not to simplify the presentation of data in the table. I need to make the correct mapping of the object from postgres to the entity.
Right now I found one solution - implement Scanner to GoogleAccount. But value in the input method is []uint8. As I can suppose, []uint8 can cast to string, and after that I can parse this string. This string (that keep in db) look like (x,x) - where x - is value. Is the right way, to parse string and set value to object? Or is way to get this result by ORM?
Is the possible way, to read this data as nested structure object?

It looks like you'll want to do two things with what you have: (1) update the model so you have the right relationship binding, and (2) use the .Preload() method if you're trying to get it to associate the data on read.
Model Changes
Gorm automatically infers relationships based on the name of the attributes in your struct and the name of the referenced struct. The problem is that Google attribute of type GoogleAccount isn't associating because gorm is looking for a type Google struct.
You're also missing a foreign key on GoogleAccount. How would the ORM know which GoogleAccount to associate with which Client? You should add a ClientId to your GoogleAccount struct definition.
Also, I would change the primary keys you're using to type uint since that's what gorm defaults to (unless you have a good reason not to use it)
If I were you, I would change my struct definitions to the following:
type Client struct {
IdClient uint `gorm:"primary_key"`
Name string
PhotoUrl string
ApprovalNumber uint16
Phone string
Password string
HoursOfNotice int8
GoogleAccount GoogleAccount // Change this to `GoogleAccount`, the same name of your struct
}
type GoogleAccount struct {
Id uint
ClientId uint // Foreign key
Token string
}
For more information on this, take a look at the associations documentation here: http://gorm.io/associations.html#has-one
Preloading associations
Now that you actually have them properly related, you can .Preload() get the nested object you want:
db.Preload("GoogleAccount").First(&user)
Using .Preload() will populate the user.GoogleAccount attribute with the correctly associated GoogleAccount based on the ClientId.
For more information on this, take a look at the preloading documentation: http://gorm.io/crud.html#preloading-eager-loading

Right now I found one solution - implement Scanner to GoogleAccount. At input of the Scan method I got []uint8, that I cast to string and parse in the end. This string (that keeping in db) look like (x,x) - where x - is value. Of course, it is not correct way to achieve my goal. But I couldn't found other solution.
I highly recommended use classical binding by relationship or simple keeping these fields in table as simplest value (not as object).
But if you would like to experiment with nested object in table, you can look at my realization, maybe it would be useful for you:
type Client struct {
// many others fields
Google GoogleAccount `json:"google"`
}
type GoogleAccount struct {
Id uint64 `json:"id"`
Token string `json:"token"`
}
func (google GoogleAccount) Value() (driver.Value, error) {
return "(" + strconv.FormatUint(google.Id, 10) + "," + google.Token + ")", nil
}
func (google *GoogleAccount) Scan(value interface{}) error {
values := utils.GetValuesFromObject(value)
google.Id, _ = strconv.ParseUint(values[0], 10, 64)
google.Token = values[1]
return nil
}

Related

GORM: Inserting *Big.Int into a NUMERIC column (postgresql)

I'm having issues getting a database insert to work with gorm.
type T struct {
PoolAddress_ string `json:"pool_address" gorm:"column:pool_address"`
BlockNumber_ uint64 `json:"blockNumber" gorm:"column:block_number"`
TxHash_ string `json:"txnHashC" gorm:"column:transaction_hash"`
TransactionHash_ string `json:"transactionHash"`
OldPrice_ *big.Int `json:"old_price" gorm:"column:old_price"`
NewPrice_ *big.Int `json:"new_price" gorm:"column:new_price"`
KeeperAddress_ string `json:"keeper_address" gorm:"column:keeper_address"`
Source_ string `json:"source" gorm:"column:source"`
GasPrice_ uint64 `json:"gasPrice" gorm:"column:gas_price"`
GasTipCap_ uint64 `json:"GasTipCap" gorm:"column:gas_tip_cap"`
GasFeeCap_ uint64 `json:"GasFeeCap" gorm:"column:gas_fee_cap"`
GasUsed_ uint64 `json:"gasUsed" gorm:"column:gas_used"`
BlockTimestamp_ uint64 `json:"blockTimestamp" gorm:"column:block_timestamp"`
}
When GORM goes to handle it I get;
[debug] Inserting into table
{000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c42778b0248d4630b7a792dddea2af5094639e9b 4351477 0xc5d846bea7a43ce79ec5dfbe6ed8565e9d82753293300581eb57175b1cfd9f04 0xc5d846bea7a43ce79ec5dfbe6ed8565e9d82753293300581eb57175b1cfd9f04 46008860000000000000000000000000000000000 45868020000000000000000000000000000000000 0x1A5E4Fc790fa234954d9AD765314676dF79aB601 Arbitrum 50000000000 50000000000 50000000000 12500000 1631463094}
Failed to insert into the database! invalid field found for struct github.com/mycelium-ethereum/myc/types/ethereum/etl/log/ocr/Tracer/v1.T's field OldPrice_, need to define a valid foreign key for relations or it need to implement the Valuer/Scanner interface
This is really frustrating because I need these fields to be bigInts but GORM doesn't seem to want to let me even though I know the NUMERIC can hold the value on the database. I've looked at the valuer/scanner interfaces but they seem to want to return basic go types which obviously wouldn't work either.
I need the values to be a numeric, I cannot store this as text/bytes/etc.

How to enumerate over columns with tokio-postgres when the field types are unknown at compile-time?

I would like a generic function that converts the result of a SQL query to JSON. I would like to build a JSON string manually (or use an external library). For that to happen, I need to be able to enumerate the columns in a row dynamically.
let rows = client
.query("select * from ExampleTable;")
.await?;
// This is how you read a string if you know the first column is a string type.
let thisValue: &str = rows[0].get(0);
Dynamic types are possible with Rust, but not with the tokio-postgres library API.
The row.get function of tokio-postgres is designed to require generic inference according to the source code
Without the right API, how can I enumerate rows and columns?
You need to enumerate the rows and columns, doing so you can get the column reference while enumerating, and from that get the postgresql-type. With the type information it's possible to have conditional logic to choose different sub-functions to both: i) get the strongly typed variable; and, ii) convert to a JSON value.
for (rowIndex, row) in rows.iter().enumerate() {
for (colIndex, column) in row.columns().iter().enumerate() {
let colType: string = col.type_().to_string();
if colType == "int4" { //i32
let value: i32 = row.get(colIndex);
return value.to_string();
}
else if colType == "text" {
let value: &str = row.get(colIndex);
return value; //TODO: escape characters
}
//TODO: more type support
else {
//TODO: raise error
}
}
}
Bonus tips for tokio-postgres code maintainers
Ideally, tokio-postgres would include a direct API that returns a dyn any type. The internals of row.rs already use the database column type information to confirm that the supplied generic type is valid. Ideally a new API uses would use the internal column information quite directly with improved FromSQL API, but a simpler middle-ground exists:-
It would be possible for an extra function layer in row.rs that uses the same column type conditional logic used in this answer to then leverage the existing get function. If a user such as myself needs to handle this kind of conditional logic, I also need to maintain this code when new types are handled by tokio-postgresql, therefore, this kind of logic should be included inside the library where such functionality can be better maintained.

How do I use Hasura/postgres uuid with apollo-ios and swift

I have a table defined in Hasura (Postgres) using native uuid as the field datatype. My goal is to create an update mutation from Swift but the uuid type is not mapped implicitly from Postgres through Hasura and Apollo to Swift. The Hasura documentation states that the uuid type supports using String but when I define the variables, in the update mutation, as a String!,
mutation RedeemMagicLinkCode($code: String!, $iosIdentifierForVendor: String!) {
__typename
update_entityname(where: {code: {_eq: $code}, iosIdentifierForVendor: {_is_null: true}}, _set: {iosIdentifierForVendor: $iosIdentifierForVendor}) {
returning {
id
}
}
}
I get an error from the Apollo code gen build step that Variable "$code" of type "String!" used in position expecting type "uuid".. When I change that to uuid!,
mutation RedeemMagicLinkCode($code: uuid!, $iosIdentifierForVendor: uuid!) {
__typename
update_en...
The code gen step completes, producing API.swift, but several instances of the compile error Use of undeclared type 'uuid' raise and are now coming from a failure during compile of the Apollo generated API.swift.
/// API.swift
...
public let operationName = "RedeemMagicLinkCode"
public var code: uuid
public var iosIdentifierForVendor: uuid
...
Can someone point me in the right direction for letting Swift know what a uuid is or defining my query so that I can pass a String as a parameter through the Apollo managed mutation? Thanks so much!
OM(Goodness), I might need to blush at how simple this solution was.
I considered the error Use of undeclared type 'uuid' plus the documentation that Hasura will accept a String for this custom scalar...
/// API+typealias.swift
public typealias uuid = String
I placed the above type alias code in a file all by itself in the folder where my GraphQL files are and voila, it worked great!
I'm not thrilled with this public type alias now hanging out at the global scope but I am thrilled with a working data service

Type wrapping of non-atomic types in golang

I'm new to golang and am trying to understand a code example of type wrapping for the "non-atomic" type time.Time.
The type extension in question is from the Go client for GDAX on github, go-coinbase-exchange project.
The expected behavior would be for Time variables from the project (coinbase.Time), which are of type Time time.Time (as defined in the project's time.go file) to behave something like the following example for extending the "atomic" type int (from blog.riff.org in that they might follow a kind of "inheritance" from the base type for functions like Time.format (from golang's standard implementation of time:
package main
import "fmt"
type Int int
func (i Int) Add(j Int) Int {
return i + j
}
func main() {
i := Int(5)
j := Int(6)
fmt.Println(i.Add(j))
fmt.Println(i.Add(j) + 12)
}
But if I modify the code example from the project's List Account Ledger example found in Readme.md to include a print function which might otherwise give me a human-readable view of the CreatedAt struct variables (as follows), I get a compiler error saying that "type coinbase.Time has no field or method Format":
for _, e := range ledger {
print("Entry Creation: ")
fmt.Printf(e.CreatedAt.Format("2006-01-02 15:04:05.999999+00"))
}
The expected behavior inside the for loop would be for it to print the ledger entries in a human-readable format. I can get the contents of the structs, but I'm not really sure how to then use the resulting wall, ext and loc members.
For example, inserting fmt.Printf("%#v", e.CreatedAt) into the for loop gives me a representation of the time that looks something like this:
coinbase.Time{wall:0x3015a123, ext:63612345678, loc:(*time.Location)(nil)}
{806986000 63638738354 <nil>}
I can also see that wall is of type uint64, that ext is of type int64 and that loc is just GMT/UTC=0 by formatting the variable as a string because fmt.Printf("%s", e.CreatedAt) gives me output which is similar to the following:
{%!s(uint64=712345678) %!s(int64=63612345678) %!s(*time.Location=<nil>)}
It seems like I'm missing something. I've requested further information through issues tab on github, but maybe this is a nube question. So I'm not sure how quick the response time would be, and I'm interested in the more general case for extending non-atomic types in go.
Named types do not inherit any methods from the underlying type (indeed there is no inheritance at all in Go); you must cast to the underlying type to call any methods from that type:
(time.Time(e.CreatedAt)).Format("2006-01-02 15:04:05.999999+00")

Javers:Ignore specific fields in Value Object while comparing two jsons

I am trying to compare two jsons, expected and the API Response using Javers, as part of testing. I want the comparison to exclude the ID parameters that are dynamically generated by response.
My VO is like
public class expectedResponse{
#DiffIgnore
private String id;
private String name;
}
Both my expectedResponse- which is read from excel file and the actual response from API are deserialized into this format and then both the responses are compared.
JsonNode expectedOutput = mapper.readTree(expected.toString());
JsonNode apiResponse = mapper.readTree(actual.toString());
diff=javers.compare(expectedOutput, apiResponse);
But this comparison doesn't exclude/ignore the ID field. Any Idea how I can get it to work? I want only the ID field excluded in comparison results, diff in name should be listed.
Also question 2> I am trying to list the changes from diff
if (diff.hasChanges())
{
List<ValueChange> changes=diff.getChangesByType(ValueChange.class);
for (ValueChange change : changes)
{
logger.info(change.getPropertyName()+ "||" +change.getLeft().toString() + "||" +change.getRight().toString());
change.getPropertyName()- doesnt print the property's name but simply prints "_value" as its value.
Can you pls help in identifying what is going wrong with the code and how can I get this fixed? I am not finding much useful documentations about Javers anywhere in google. Any help is appreciated.
You should compare you domain object instead of object with JsonNode class, look that #DiffIgnore annotation is present only in your domain class and there is no connection between JsonNode and ExpectedResponse, thats why Javers doesn't know to ignore this field.
To summarise, your code should looks like this:
ExpectedResponse expectedOutput = ...
ExpectedResponse apiResponse = ...
diff=javers.compare(expectedOutput, apiResponse);