How to insert a GORM model with deeply nested one-to-many models - go-gorm

I have 3 struct models corresponding to 3 DB tables.
Book model
type Book struct {
ID uuid.UUID
Pages []*Page
}
Page model
type Page struct {
ID uuid.UUID
BookID string
PageNum int
Words []*Word
}
Word model
type Word struct {
ID uuid.UUID
PageID uuid.UUID
Letters []byte
}
One Book can have many Pages. One Page can have many Words.
I am trying to insert a single hydrated Book struct into my Book table, and allow GORM to populate the several rows for the Page and Word tables. Based on the examples in the GORM has many documentation it seems this can be done with one create() call and association will be inferred by the default naming convention. But when I call db.Create(&hydratedBook) only the Book table is inserted into.
Debug output:
INSERT INTO "book" ("id","created_at","updated_at","delete_requested_at")
VALUES ('95B9A964-7227-44B1-AA32-C5BCEDCE0857','2022-07-08 22:01:36','2022-07-08 22:01:36',NULL)
RETURNING "book"."id"
Any feedback is greatly appreciated.

Related

Secondary Index on Custom Java Object in Aerospike

I have two classes.
class A {
String aName;
B b;
public A(String aName, B b) {
this.aName = aName;
this.b = b;
}
public String getaName() {
return aName;
}
public B getB() {
return b;
}
}
class B {
String bName;
public B(String bName) {
this.bName = bName;
}
public String getbName() {
return bName;
}
}
I am storing A as a set in Aerospike and A.aName is primary key. I want a secondary key on A.b. I have created index on A.b attribute and able to persist also. But search from the index is not returning anything. As per my understanding, Aerospike supports only three type of indexes: String, Numeric and Geo,. Is there any option for custom object.
Actually you can also index string, numeric and geo within different source types - basic (meaning just a bin with scalar data), list (so you can index strings or numeric data that is contained in a list), map keys and map values.
See: https://www.aerospike.com/docs/guide/query.html#secondary-index-key-source-type
You could model this in a few ways:
As a map. Let's assume that you store the value of A in a bin whose type is a map, it could have two map-keys - aName and bName. You can build a secondary index for string data on map values. Now, if you search for a specific bName you'll have this record come up.
More rationally you'd do the following as separate bins.
Assume that you use several bins, among them two bins to hold aname and bname. Their values are strings. You could now build a secondary index for string values and a basic index type (string data not contained in a complex data type such as list or map). You'd query for all the records where the predicate is bname = foo.
For a more complex situation one or more bname values map to a single aname, you'd actually model this as a lookup table.
Assume a set (table) called users holding records whose keys are the aname. A single key-value operation such as a read or an upsert works on a specific instance of class A, identified by a given aname. One record in Aerospike per instance of A.
You have another set (table) as the lookup table, where for each unique bname you create a record. The specific bname is the key to this record. The value is the aname. So to find the record in the users set, you first look it up in the lookup table by the bname. You use the value of this record as the key for the users record for that aname. This is the most common way of modeling this type of use case, without a secondary index.
Here is the answer post on Aerospike forum.
Binary blobs can be anything, there’s definitely no way to index that. If there is a particular field in your class you can pull out and set as a separate bin with a string/long type then that would work
https://discuss.aerospike.com/t/secondary-index-on-custom-java-object/6485

Grails displaying values from two tables using primary and foreign keys

This is, I hope, probably quite obvious but I can't find an example that I think answers my issue.
I have an SQL database that I cant modify and within it are two tables, linked with primary/foreign keys (test_scenario and test_exec_queue respectively, so the PK value from test_scenario can show up many times within test_exec_queue) and when I display the data on screen I want it to, instead of displaying the FK value from test_exec_queue I want it to use that to get testScenarioName from the test_scenario table and display that instead.
So far my class looks like this but I've no idea what to put in to do the above logic, or do I do this somewhere else? In the controller? Any help appreciated
class TestExecQueue {
static constraints = {
testscenarioid(blank:false, editable:false)
myPriority(inList:[0,1,2,3,4], blank:false)
myState(inList:["READY"], blank:false)
}
static mapping = {
table "test_exec_queue"
version false
columns{
id column:"test_exec_queue_id"
testscenarioid column:"test_scenario_id"
myPriority column:"Priority"
myState column:"State"
}
}
Integer testscenarioid
Integer myPriority
String myState
}
You need to create a class which maps the test_scenario table in addition to the TestExecQueue class you've already implemented.
In your TestExecQueue class, you would link to the scenario by class, rather than by an integer field:
class TestExecQueue {
static mapping = {
scenario column:'test_scenario_id'
}
TestScenario scenario
}
Note: This is one example of mapping the relationship, you should review the Domain Modeling section of the Grails Documentation for other options.
The display of the classes is entirely dependent on your controllers and views, and that would need more elaboration on your part to answer clearly. One option is to set the public toString() methods on the classes that will be printed.

nhibernate collection query with private backing field

I have a mant-to-many relationship modeled in the database (with a bridge table) between Student and Professor (_students_selected) , in my entites i have modeled it as a one-to-many relationship i.e. a Professor has one Student.
HasManyToMany<Student>(Reveal.Member<Professor>("_students"))
.Table("_students_selected").ChildKeyColumn("student_key").ParentKeyColumn("professor_key");
public class Professor
{
private IList<Students> _students;
public virtual Student Student
{
get { return _students.FirstOrDefault(); }
}
}
The above works when getting the data however when querying over the Professors i am unable to add a where condition on the students because the actual data is mapped to the private backing field _students. How do i query this? code below does not work.
_unitOfWork.Session.QueryOver<Professor>().Where(i => i.Student.Id == 24).List();
NHibernate can't translate your C# code inside the property to SQL, it can only work with mapped properties. Either use the collection in the statement (which needs to be public/internal then of course) or filter the results in memory (but be careful with select n + 1 problems then).

How to do a full text search with Nhibernate and get result ranks?

I have a SQL 2008 R2 database with full text indexing set up and would like to use NHibernate to get back search results with ranking. I've figured out the SQL queries usng FULLTEXTTABLE to get result rankings, but I'm struggling with how to use NHibernate to get results with the ranking value since it doesn't map to an actual column in any table.
Any pointers?
(First of all the following syntax will be a bit hazy because this is from memory, please check the api)
well you can either construct some DTO class and map that on-the-fly
for example:
public class Person
{
public virtual String Name {get;set;}
public virtual String Surname {get;set;}
}
which is properly mapped to nhibernate
and the
PersonDTO : Person
{
public int FTSRanking {get;set;}
}
which is not mapped. Note that i'm inheriting from class Person although that is not necessary and i'm only doing it for ease.
This PersonDTO class is only used on queries but there are limitations, as the following hql shows.
NHSes.CreateQuery('select p.Name, p.Surname, p.FTSAlias as FTSRanking from Person p')
.SetResultTransformer(Transformers.AliasToBean<PersonDTO>())
will return a PersonDTO which nhibernate manages to assemble because every item in the select list matches a property (in name, casing, type) in the PersonDTO class. Also you will have to manually type the select list and also, since it is not a mapped class, nhibernate cannot assemble collections.
An other option would be to use the Criteria API in which you set projections (aka extend the select list)
IList<object[]> results = NHSes.CreateCriteria(typeof(Person))
.Add(Expression.SQL(" your fts clause here "))
.SetProjection(Projections.SQL(" add your fts ranking column here",,), Projections.( here add as a projection the main entity ))
.List<object[]>();
where in the results variable each returned row is an object[] and the first element (ie results[0][0]) is the ranking and the second element (ie results[0][1]) is a properly managed Person object

Accept Interface into Collection (Covariance) troubles with nHibernate

I am using Fluent nHibernate for my persistence layer in an ASP.NET MVC application, and I have come across a bit of a quandry.
I have a situation where I need to use an abstraction to store objects into a collection, in this situation, an interface is the most logical choice if you are looking at a pure C# perspective.
Basically, an object (Item) can have Requirements. A requirement can be many things. In a native C# situation, I would merely accomplish this with the following code.
interface IRequirement
{
// methods and properties neccessary for evaluation
}
class Item
{
virtual int Id { get; set; }
virtual IList<IRequirement> Requirements { get; set; }
}
A crude example. This works fine in native C# - however because the objects have to be stored in a database, it becomes a bit more complicated than that. Each object that implements IRequirement could be a completely different kind of object. Since nHibernate (or any other ORM that I have discovered) cannot really understand how to serialize an interface, I cannot think of, for the life of me, how to approach this scenario. I mean, I understand the problem.
This makes no sense to the database/orm. I understand completely why, too.
class SomeKindOfObject
{
virtual int Id { get; set; }
// ... some other methods relative to this base type
}
class OneRequirement : SomeKindOfObject, IRequirement
{
virtual string Name { get; set; }
// some more methods and properties
}
class AnotherKindOfObject
{
virtual int Id { get; set; }
// ... more methods and properties, different from SomeKindOfObject
}
class AnotherRequirement : AnotherKindOfObject, IRequirement
{
// yet more methods and properties relative to AnotherKindOfObject's intentive hierarchy
}
class OneRequirementMap : ClassMap<OneRequirement>
{
// etc
Table("OneRequirement");
}
class AnotherRequirementMap : ClassMap<AnotherRequirement>
{
//
Table("OtherRequirements");
}
class ItemMap : ClassMap<Item>
{
// ... Now we have a problem.
Map( x => x.Requirements ) // does not compute...
// additional mapping
}
So, does anyone have any ideas? I cannot seem to use generics, either, so making a basic Requirement<T> type seems out. I mean the code works and runs, but the ORM cannot grasp it. I realize what I am asking here is probably impossible, but all I can do is ask.
I would also like to add, I do not have much experience with nHibernate, only Fluent nHibernate, but I have been made aware that both communities are very good and so I am tagging this as both. But my mapping at present is 100% 'fluent'.
Edit
I actually discovered Programming to interfaces while mapping with Fluent NHibernate that touches on this a bit, but I'm still not sure it is applicable to my scenario. Any help is appreciated.
UPDATE (02/02/2011)
I'm adding this update in response to some of the answers posted, as my results are ... a little awkward.
Taking the advice, and doing more research, I've designed a basic interface.
interface IRequirement
{
// ... Same as it always was
}
and now I establish my class mapping..
class IRequirementMap : ClassMap<IRequirement>
{
public IRequirementMap()
{
Id( x => x.Id );
UseUnionSubclassForInheritanceMapping();
Table("Requirements");
}
}
And then I map something that implements it. This is where it gets very freaky.
class ObjectThatImplementsRequirementMap : ClassMap<ObjectThatImplementsRequirement>
{
ObjectThatImplementsRequirementMap()
{
Id(x => x.Id); // Yes, I am base-class mapping it.
// other properties
Table("ObjectImplementingRequirement");
}
}
class AnotherObjectThatHasRequirementMap : ClassMap<AnotherObjectThatHasRequirement>
{
AnotherObjectThatHasRequirementMap ()
{
Id(x => x.Id); // Yes, I am base-class mapping it.
// other properties
Table("AnotheObjectImplementingRequirement");
}
}
This is not what people have suggested, but it was my first approach. Though I did it because I got some very freaky results. Results that really make no sense to me.
It Actually Works... Sort Of
Running the following code yields unanticipated results.
// setup ISession
// setup Transaction
var requirements = new <IRequirement>
{
new ObjectThatImplementsRequirement
{
// properties, etc..
},
new AnotherObjectThatHasRequirement
{
// other properties.
}
}
// add to session.
// commit transaction.
// close writing block.
// setup new session
// setup new transaction
var requireables = session.Query<IRequirable>();
foreach(var requireable in requireables)
Console.WriteLine( requireable.Id );
Now things get freaky. I get the results...
1
1
This makes no sense to me. It shouldn't work. I can even query the individual properties of each object, and they have retained their type. Even if I run the insertion, close the application, then run the retrieval (so as to avoid the possibility of caching), they still have the right types. But the following does not work.
class SomethingThatHasRequireables
{
// ...
public virtual IList<IRequirement> Requirements { get; set; }
}
Trying to add to that collection fails (as I expect it to). Here is why I am confused.
If I can add to the generic IList<IRequirement> in my session, why not in an object?
How is nHibernate understanding the difference between two entities with the same Id,
if they are both mapped as the same kind of object, in one scenario, and not the other?
Can someone explain to me what in the world is going on here?
The suggested approach is to use SubclassMap<T>, however the problem with that is the number of identities, and the size of the table. I am concerned about scalability and performance if multiple objects (up to about 8) are referencing identities from one table. Can someone give me some insight on this one specifically?
Take a look at the chapter Inheritance mapping in the reference documentation. In the chapter Limitations you can see what's possible with which mapping strategy.
You've chose one of the "table per concrete class" strategies, as far as I can see. You may need <one-to-many> with inverse=true or <many-to-any> to map it.
If you want to avoid this, you need to map IRequirement as a base class into a table, then it is possible to have foreign keys to that table. Doing so you turn it into a "table per class-hierarchy" or "table per subclass" mapping. This is of course not possible if another base class is already mapped. E.g. SomeKindOfObject.
Edit: some more information about <one-to-many> with inverse=true and <many-to-any>.
When you use <one-to-many>, the foreign key is actually in the requirement tables pointing back to the Item. This works well so far, NH unions all the requirement tables to find all the items in the list. Inverse is required because it forces you to have a reference from the requirement to the Item, which is used by NH to build the foreign key.
<many-to-any> is even more flexible. It stores the list in an additional link table. This table has three columns:
the foreign key to the Item,
the name of the actual requirement type (.NET type or entity name)
and the primary key of the requirement (which can't be a foreign key, because it could point to different tables).
When NH reads this table, it knows from the type information (and the corresponding requirement mapping) in which other tables the requirements are. This is how any-types work.
That it is actually a many-to-many relation shouldn't bother you, it only means that it stores the relation in an additional table which is technically able to link a requirement to more then one item.
Edit 2: freaky results:
You mapped 3 tables: IRequirement, ObjectThatImplementsRequirement, AnotherObjectThatHasRequirement. They are all completely independent. You are still on "table per concrete class with implicit polymorphism". You just added another table with containing IRequirements, which may also result in some ambiguity when NH tries to find the correct table.
Of course you get 1, 1 as result. The have independent tables and therefore independent ids which both start with 1.
The part that works: NHibernate is able to find all the objects implementing an interface in the whole database when you query for it. Try session.CreateQuery("from object") and you get the whole database.
The part that doesn't work: On the other side, you can't get an object just by id and interface or object. So session.Get<object>(1) doesn't work, because there are many objects with id 1. The same problem is with the list. And there are some more problems there, for instance the fact that with implicit polymorphism, there is no foreign key specified which points from every type implementing IRequirement to the Item.
The any types: This is where the any type mapping comes in. Any types are stored with additional type information in the database and that's done by the <many-to-any> mapping which stores the foreign key and type information in an additional table. With this additional type information NH is able to find the table where the record is stored in.
The freaky results: Consider that NH needs to find both ways, from the object to a single table and from the record to a single class. So be careful when mapping both the interface and the concrete classes independently. It could happen that NH uses one or the other table depending on which way you access the data. This may have been the cause or your freaky results.
The other solution: Using any of the other inheritance mapping strategies, you define a single table where NH can start reading and finding the type.
The Id Scope: If you are using Int32 as id, you can create 1 record each second for 68 years until you run out of ids. If this is not enough, just switch to long, you'll get ... probably more then the database is able to store in the next few thousand years...