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.
Related
I need to get a document from MongoDB and put it into a struct with custom type.
type translate_t struct {
Sources []string `bson:"sources"`
Targets []string `bson:"targets"`
}
type practice_t struct {
Id primitive.ObjectID `bson:"_id"`
Translate translate_t `bson:"translate"`
}
The data in the database is as expected.
"practice": {
"translate": {
"sources": ["data", "more data"]
"target": ["even", "more", "data"]
}
}
What I do (very basic):
var item practice_t
err = collection.FindOne(ctx, filter).Decode(&item)
log.Printf("item:%+v", item)
The log prints this:
{Id:ObjectId("5deeblablabla"), Translate:{Sources:[] Targets:[]}} //where is my sweet data?
Now, I want to point out that all the other items (not nested with custom struct) get decoded properly. So, it seems to me that the Decode() function doesn't like custom structs...
This looks like a very common task, so am I missing anything?
I've been reading about overriding the default Decoder or something like that, but that seems way too much work for something this simple.
You are missing "practice":
type doc struct {
Practice practice_t `bson:"practice"`
}
The database document has to structurally match the document you're unmarshaling. You showed the database document is an object with a practice field. Then you must have a struct with a field tagged with practice.
I am using lighthouse-php as Api Gateway in a micro services architecture.
So for all my types I make a request internally through Guzzle.
But I am needing to implement filters that are suitable for any type and that give flexibility when making queries.
I need to implement a query like this:
query news (
order_by: {publication_date: desc}
where: {
_or: {categories_id: { _eq: 1 }, title: { _ilike: "news" } }
}
limit: 10
offset: 20
) {
id
category_name: name
photo
publication_date
text
title
}
But I have no idea how to implement this "where" filter that receives a composite object as in this example.
Remember that this query will not use any model within lumen, since it will be a custom query that will make a request to the microservice of news.
What I need is the way that my query receives whatever comes in where, limit and order, to send it on request. But I have no idea how to build something like this in the scheme.
Anyone have any idea how to do it?
Thanks friends.
Yes, you can.
Just now I'm making an component that will receive criterias to filter in graphql query so I need to fill filter's where params with those criterias.
Imagine the following schema:
type News{
id: ID!
title: String!
views: Int!
}
type Query{
getNews(where: _ #whereConditions(columns:["title", "views"])) : [News!] #all
}
We can make a query and fill where variables later
query GetNews($whereNews: [GetNewsWhereWhereConditions!]){
getNews(where: {OR: $whereNews}){
title
views
}
}
When querying we can fill the variables sending an object like
{
"where":[
{"column": "TITLE", "operator": "LIKE", "value": "Amazing title"},
{"column": "VIEWS", "operator": "GTE", "value": 10,
]
}
Loopback has a way to make a light relationship using referencesMany where you can say something like:
{
"name": "SomeModel",
"plural": "SomeModel",
// ...,
"relations": {
"images": {
"type": "referencesMany",
"model": "Images",
"options": {
"validate": true
}
}
}
}
Which will allow you to store an array of ObjectId in MongoDB.
I can then do something like:
SomeModel.find({ include: 'images' }) or GET to /api/SomeModel/?filter[include]=images to include a response with nested image objects that are related to the SomeModel.
Is there a good way to do this in a singular case (not an array of values)? Relate one parent to a child? HasOne puts a someModelId on the child and I don't really want to pollute the Image model with BelongsTo as its polymorphic and belongs to all sorts of stuff.
I have here a Building Object where inside sits an Array of Floor Objects.
When Projecting, my goal is to return or count the number of Floor Objects inside a Building Object after matching the elements accordingly. The code is as follows:
Objects:
type Floor struct {
// Binary JSON Identity
ID bson.ObjectId `bson:"_id,omitempty"`
// App-level Identity
FloorUUID string `bson:"f"`
// Floor Info
FloorNumber int `bson:"l"`
// Units
FloorUnits []string `bson:"u"`
// Statistics
Created time.Time `bson:"y"`
}
type Building struct {
// Binary JSON Identity
ID bson.ObjectId `bson:"_id,omitempty"`
// App-level Identity
BldgUUID string `bson:"b"`
// Address Info
BldgNumber string `bson:"i"` // Street Number
BldgStreet string `bson:"s"` // Street
BldgCity string `bson:"c"` // City
BldgState string `bson:"t"` // State
BldgCountry string `bson:"x"` // Country
// Building Info
BldgName string `bson:"w"`
BldgOwner string `bson:"o"`
BldgMaxTenant int `bson:"m"`
BldgNumTenant int `bson:"n"`
// Floors
BldgFloors []Floor `bson:"p"`
// Statistics
Created time.Time `bson:"z"`
}
Code:
func InsertFloor(database *mgo.Database, bldg_uuid string, fnum int) error {
fmt.Println(bldg_uuid)
fmt.Println(fnum) // Floor Number
var result Floor // result := Floor{}
database.C("buildings").Find(bson.M{"b": bldg_uuid}).Select(
bson.M{"p": bson.M{"$elemMatch": bson.M{"l": fnum}}}).One(&result)
fmt.Printf("AHA %s", result)
return errors.New("x")
}
It turns out, no matter how I try the query returns a Building Object, not a floor object? What changes do I need to make in order to have the query fetch and count Floors and not Buildings?
This is done so to check if a Floor inside Building already exists before insertion. If there's a better a approach then I'll replace mine with the better!
Thanks!
You are querying for a Building document so mongo returns that to you even though you try to mask some of its fields using projection.
I don't know of a way to count the number of elements in a mongo array in a find query, but you can use the aggregation framework, where you have the $size operator that does exactly this. So you should send a query like this to mongo :
db.buildings.aggregate([
{
"$match":
{
"_id": buildingID,
"p": {
"$elemMatch": {"l": fNum}
}
}
},
{
"$project":
{
nrOfFloors: {
"$size": "$p"
}
}
}])
Which in go it would look like
result := []bson.M{}
match := bson.M{"$match": bson.M{"b": bldg_uuid, "p": bson.M{"$elemMatch": bson.M{"l": fNum}}}}
count := bson.M{"$project": bson.M{"nrOfFloors": bson.M{"$size": "$p"}}}
operations := []bson.M{match, count}
pipe := sess.DB("mgodb").C("building").Pipe(operations)
pipe.All(&result)
If I have an incoming JSON of following structure
[
{
"personId" : 12,
"name": "John Doe",
"age": 48,
"birthdate": "12/1/1954",
"relationships": [
{
"relationType":"parentOf",
"value" : "Johnny walker"
},
{
"relationType":"sonOf",
"value" : "Charles Smith"
}
]
},
{
"personId" : 13,
"name": "Merry Christmas",
"age": 28,
"birthdate": "12/1/1985",
"relationships": [
{
"relationType":"sisteOf",
"value" : "Will Smith"
},
{
"relationType":"cousinOf",
"value" : "Brad Pitt"
}
]
}
]
And requirement is that for each Person record controller will have to carve out relationships array and store each record from it in a separate relationship table with personId association while persisting this incoming JSON.
And subsequently when querying these persons records system will have to lookup relationships for each person from relationships table and inject them to form the same above looking JSON to give back to UI for rendering.
What's the best efficient way to perform this "carve out" and later "inject" operations using Play framework in Scala? (using Slick in persistent layer APIs) I have looked at this JSON transformation link and json.pickBranch in there but not quite sure if that'll be fully applicable here for "carve out" and "inject" use cases for preparing JSON shown in the example. are there any elegant ideas?
One way, which is pretty straightforward, is to use case classes along with Play Json inceptions
import play.api.libs.json.Json
case class Relationship(relationType: String, value: String)
object Relationship {
implicit val RelationshipFormatter = Json.format[Relationship]
}
case class Person(personId: String, name: String, age: Int, birthdate: String, relationships: Seq[Relationship]) {
def withRelationships(relationship: Seq[Relationship]) = copy(relationships = relationships ++ relationship)
}
object Person {
implicit val PersonFormatter = Json.format[Person]
}
Now you can convert a json value to Person by using the following code, provided that jsValue is a json value of type JsValue (which in play controllers you can get by request.body.asJson):
Json.fromJson[Person](jsValue)
In Play controllers, you can
For converting a Person to json you can use the following code provided that person is a value of type Person in your context:
Json.toJson(person)
The only remaining thing is your Slick schemas which is pretty straight forward.
One option is to use a simple schema for Person, without relations and one schema for Relation with a foreign key to Person table. Then you need to find all relations associated with a specific Person, namely person and then append them to that person by calling the withRelationships method which gives you a new Person which you can serve as json:
val person = .... // find person
val relations = .... // find relationships associated with this person
val json = Json.toJson(person.withRelationships(relations))