So I am using go-gorp to query postgres and I can't seem to query the composite type inside my table is giving an error. All I want is appropriately nested JSON response. My postgres schema is:
CREATE TYPE PhoneType AS ENUM ('MOBILE', 'HOME', 'WORK');
CREATE TYPE PhoneNumber AS (
"Number" VARCHAR,
"Type" PhoneType
);
CREATE TABLE Person (
"Id" SERIAL PRIMARY KEY NOT NULL,
"Name" VARCHAR NOT NULL,
"Email" VARCHAR,
"Number" PhoneNumber[]
);
Correspondingly in golang, I have
const (
MOBILE PhoneType = iota
HOME
WORK
)
type PhoneNumber struct {
Number string
Type PhoneType
}
type Person struct {
Id int
Name string
Email string
PhoneNumber // not sure how to get array of phone numbers here
}
And to query, I am using go-gorp as follows:
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.PostgresDialect{}}
var data []Person
_, err := dbmap.Select(&data, "SELECT * FROM Person")
the result I get has the format
{
"Id":
"Name":
"Email"
"Number": "{\"(..., ...)\"}"
"Type": 0
}
what I'm expecting is:
{
"Id":
"Name":
"Email":
"PhoneNumber": [{
"Number":
"Type":
}]
}
How can I change this nested composite type to the type friendly to my declaration in go?
EDIT: Looks like postres arrays and composite types become strings in go. How can I redesign the schema to achieve similar result?
your type declaration is wrong, try this for example:
type Phone struct {
Number string
Type PhoneType
}
type Person struct {
Id int
Name string
Email string
PhoneNumber []Phone
}
Related
I am trying to implement Appsync with Aurora RDS. Get query returns response with all the attribute values as null. I think it is able to connect with the DB properly because I have seen some error when I deliberately misspelled the table name. I am not sure where the problem is.
I tried the same implementation with Dynamodb and it worked fine. Is this some problem with resolver or something related to permissions?
Response looks like this:
Response:
{
"data": {
"getTest": {
"id": null,
"name": null,
"surname": null
}
}
}
DB table description with:
id int(11)
name text
surname text
AppSync GraphQL schema is:
type Query {
getTest(id: ID!): Test
}
type Test {
id: ID
name: String
surname: String
}
schema {
query: Query
}
Request resolver:
{
"version": "2018-05-29",
"statements": [
"select * from TestTable where id = '$ctx.args.id'"
]
}
Response resolver:
#if($ctx.error)
$utils.error($ctx.error.message, $ctx.error.type)
#end
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
I was able to fix my issue. The problem was with referencing the result in response resolver. It worked after adding another [0] to the result object.
#if($ctx.error)
$utils.error($ctx.error.message, $ctx.error.type)
#end
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])
I'm following the tutorials https://quarkus.io/guides/rest-data-panache and https://quarkus.io/guides/mongodb-panache to implement a simple MongoDB entity and resource with Quarkus Panache MongoDB.
Here's what I have so far:
#MongoEntity(collection = "guests")
class GuestEntity(
var id: ObjectId? = null,
var name: String? = null
)
#ApplicationScoped
class GuestRepository: PanacheMongoRepository<GuestEntity>
interface GuestResource: PanacheMongoRepositoryResource<GuestRepository, GuestEntity, ObjectId>
When running this, I can create a document by calling
POST localhost:8080/guest
Content-Type: application/json
{
"name": "Foo"
}
The response contains the created entity
{
"id": {
"timestamp": 1618306409,
"date": 1618306409000
},
"name": "Foo"
}
Notice, how the id field is an object whereas I would like it to be a string.
It turns out that the application was using quarkus-resteasy instead of quarkus-resteasy-jackson.
Once the proper dependency was in place, everything worked as expected
To serialize the id field as a String, apply the following annotation to the id field:
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import io.quarkus.mongodb.panache.jackson.ObjectIdSerializer
#MongoEntity(collection = "guests")
class GuestEntity(
// important: apply the annotation to the field
#field:JsonSerialize(using = ObjectIdSerializer::class)
var id: ObjectId? = null,
var name: String
)
Now the response is
{
"id": "607567590ced4472ce95be23",
"name": "Foo"
}
I want to load and cast a structure from a mongoDB with a few different document types that can nest them-selfes. Let's say we have three different struct types, with one parent and two children
type Parent struct {
ID bson.ObjectId `bson:"_id,omitempty`
Title string
Description string
NestedObjects []*Parent
}
type ChildA struct {
Parent
DateFrom time.Time
DateTo time.Time
}
type ChildB struct {
Parent
Options []string
}
The idea is to have a root-document holding other ChildA or ChildB documents. So an example document could look like this:
{
"Title": "test event",
"Description": "description goes here",
"NestedObjects": [
{
"Title": "test event",
"Description": "description goes here",
"NestedObjects": [],
"Options": ["foo", "bar"]
}
],
DateFrom: ISODate(...),
DateTo: ISODate(...)
}
How can I now cast them dynamically into a struct (of the correct type)? The root-document is of the Type ChildA and the first nested document of the type ChildB. And the result I want is some kind of static type []Parent which can be dynamically be casted to the sub types, somehow like this (pseudo-code)
for _ ele := range results {
if ele typeof ChildA {
...
}
if ele typeof ChildB {
...
}
}
(Just for some explaination: I want to build events like ToDo Lists, Polls, and some others which can contain each other. So like a event "Netflix&chill" can have a Poll & ToDos, example: ToDos: "Buy popcorn", "Decide which movie to watch -> [Poll: "Jurassic Park", "Inception", ...]".)
First of all your NestedObjects []*Parent is a slice of Parents, not children.
Secondly, it is not clear how you are going to discriminate your children and what you would expect in the result of, for example, Collection.Find().One(?)
It may worth to read Unstructured MongoDB collections with mgo for good examples of how to use bson.* types.
I will go with an example.
Say I have three tables defined like this:
(pseudocode)
Realm
id: number, pk
name: text, not null
Family
id: number, pk
realm_id: number, fk to Realm, pk
name: text, not null
Species
id: number, pk
realm_id: number, fk to Family (and therefore to Realm), pk,
family_id: number, fk to Family, pk,
name: text, not null
A temptative case classes definition would be
case class Realm (
id: Int,
name: String
)
case class Family (
id: Int,
realm: Realm,
name: String
)
case class Species (
id: Int,
family: Family,
name: String
)
If I make a json out of this after querying the database it would look like this:
SELECT *
FROM realm
JOIN family
ON family.realm_id = realm.id
JOIN species
ON species.family_id = family.id
AND species.realm_id = family.realm_id
Example data:
[{
"id": 1,
"family": {
"id": 1,
"name": "Mammal",
"realm": {
"id": 1,
"name": "Animal"
}
},
"name": "Human"
},
{
"id": 2,
"family": {
"id": 1,
"name": "Mammal",
"realm": {
"id": 1,
"name": "Animal"
}
},
"name": "Cat"
}]
Ok, so far... This is usable, if I need to show every species grouped by realm, I would transform the JsValue or in javascript do filters, etc. However when posting data back to the server, these classes seem a little awkward. If I want to add a new species I would have to post something like this:
{
"id": ???,
"family": {
"id": 1,
"name": "Mammal", // Awkward
"realm": {
"id": 1,
"name": "Animal" // Awkward
}
},
"name": "Cat"
}
Should my classes be then:
case class Realm (
id: Int,
name: Option[String]
)
case class Family (
id: Int,
realm: Realm,
name: Option[String]
)
case class Species (
id: Option[Int],
family: Family,
name: String
)
Like this, I can omit posting what it seems to be unnecesary data, but then the classes definition don't reflect what is in the database which are not nullable fields.
Queries are projection of data. More or like Table.map(function) => Table2. When data is extracted from the database and I don't get the name field, it doesn't mean it is null. How do you deal with this things?
One way to deal with it is to represent the interconnection using other data structures instead of letting each level know about the next.
For example, in the places where you need to represent the entire tree, you could represent it with:
Map[Realm, Map[Family, Seq[Species]]]
And then just Realm in some places for example as a REST/JSON resource, and maybe (Species, Family, Realm) in some place where you only want to work with one species but need to know about the other two levels in the hierarchy.
I would also advice you to think two or three times about letting your model structure define your JSON structure, what happens with the codes that consumes your JSON when you change anything in your model classes? (And if you really want that, do you actually need to go via a model structure, why not build your JSON directly from the database results and skip one level of data transformation?)
Grails makes it easy to get a domain object by ID (handy for building a REST API).
A controller to retrieve a resource can be as simple as:
MetricController.groovy
import grails.converters.JSON
class MetricController {
def index() {
def resource = Metric.get(params.id)
render resource as JSON
}
}
When using the Grails plugin for MongoDB GORM (compile ":mongodb:1.2.0"), the default id type of Long needs to be changed to type String or ObjectId.
Metric.groovy
import org.bson.types.ObjectId
class Metric {
static mapWith = "mongo"
ObjectId id
String title
}
However, doing a .get(1) will now result in:
Error 500: Internal Server Error
URI
/bow/rest/metric/1
Class
java.lang.IllegalArgumentException
Message
invalid ObjectId [1]
I took a guess and changed the controller to use findById:
def resource = Metric.findById(new ObjectId(new Date(), params.id.toInteger()))
That fixed the error, but it fails to find the object (always returns null).
For example, using the id of "-1387348672" does not find this test object:
{ "class" : "Metric",
"id" : { "class" : "org.bson.types.ObjectId",
"inc" : -1387348672,
"machine" : 805582250,
"new" : false,
"time" : 1371329632000,
"timeSecond" : 1371329632
},
"title" : "Test"
}
The ObjectId.inc field may not even be the correct field to use for the resource ID.
So, what is the simplest way to retrieve a domain object by ID when using MongoDB?
When a domain object is persisted in MongoDB, it is stored as a document with an ObjectId as a unique 12 bytes BSON primary key. For example, if you have a domain object Product like
import org.bson.types.ObjectId
class Product {
ObjectId id
String name
static mapWith = "mongo"
}
then the persisted entity in MongoDB would look like below if you save with a name as "TestProduct".
//db.product.find() in mongodb
{
"_id" : ObjectId("51bd047c892c8bf0b3a58b21"),
"name" : "TestProduct",
"version" : 0
}
The _id becomes your primary key for that document. To get this document you need the primary key ObjectId. Speaking from a RESTful context, you atleast need the 12 bytes hexcode 51bd047c892c8bf0b3a58b21 as part of the request.
So in the above case, you can fetch that particular document by doing something like
Product.get(new ObjectId("51bd047c892c8bf0b3a58b21"))
Product.findById(new ObjectId("51bd047c892c8bf0b3a58b21"))
Have a look at the API for ObjectId which would make clear how to retrieve a document.
When you retrieve the document as JSON, then it just shows the ObjectId class with its elements.
{
"class": "com.example.Product",
"id": {
"class": "org.bson.types.ObjectId",
"inc": -1280996575,
"machine": -1993569296,
"new": false,
"time": 1371341948000,
"timeSecond": 1371341948
},
"name": "TestProduct"
}
For completeness, here's the domain with a controller to get a resource by ID string (instead of ObjectID).
Example:
Metric.get("51bf8ccc30040460f5a05579")
Domain
import org.bson.types.ObjectId
class Metric {
ObjectId id
String title
static mapWith = "mongo"
def out() {
return [
id: id as String, //convert Mongo ObjectId to 12-byte hex BSON
title: title
]
}
}
The out() method is used to render the resource showing its ID string (not its ObjectID).
Controller
import grails.converters.JSON
class MetricController {
def index() {
def resource = Metric.read(params.id)
render resource.out() as JSON
}
}
Example JSON response for /rest/metric/51bf8ccc30040460f5a05579
{ "id": "51bf8ccc30040460f5a05579", "title": "Accounts" }