Neo4j.rb returns models with incorrect class for nodes with multiple labels - neo4j.rb

I have two ActiveNode models:
class Company
include Neo4j::ActiveNode
end
and
class Entity
include Neo4j::ActiveNode
end
They correspond to the labels "Entity" and "Company", which are attached to the same node. So, a node and be an entity and a company.
In my console, when I attempt the following query:
Entity.where(entity_id: 1).first
It returns a Company object:
#<Company uuid: entity_id: 1>
I don't want that. If I ask for an entity, I want an entity returned. The Entity model have different methods defined than the Company model. Is there anyway I can enforce the correct behavior? It seems pretty pretty counter intuitive that it behaves in this way.
I am using neo4j 3.0 and neo4j.rb 7.0.3

This is a good point. If both labels could be matched, it should use the one for the class which was used to do the find.
I'm curious about your modeling, though. Can a Company node ever not be an Entity or vice versa? Or is, for example, a Company always a kind of an Entity? If so, you might want to use inheritence:
class Entity
include Neo4j::ActiveNode
end
class Company < Entity
# No need to include Neo4j::ActiveNode
end
But it's partially a question of if it makes sense for Company nodes to inherit the behavior/logic of Entity

Related

Multiple types for each record in Neo4j.rb

I have a database currently represented as a set of YAML files (one record per file). I would like to port it into Neo4j. Each record has a property "type" which stores an array of types. I would like to have a module (that includes ActiveNode) for each type. Each node object would then extend the modules corresponding to its types. The only way I can think of to implement this with neo4j.rb is to generate a class for each existing combination of types and include the corresponding type modules in the class. Is there some better way to accomplish this?
More concrete examples might help. Is there a natural hierarchy to the types?
Class hierarchy for multiple labels has been supported for a while, but I just put in some changes to the master branch in the last couple of days to make it work more smoothly. You should be able to do something like this:
class Person
include Neo4j::ActiveNode
end
class Author < Person
end
class Collaborator < Person
end
class Software
include Neo4j::ActiveNode
end
class Application < Software
end
class Library < Software
end
If you did ChildType.create it would create a node with both the ParentType and ChildType labels. If a query loads a node with both labels, the ChildType model class will be used.
We've also talked about the ability to load modules to do multiple labels, though we weren't able to think of a good example, so I'd welcome one.

Self-referential symmetric many-to-many

I'm quite stumped trying to model self-referential, many-to-many, symmetrical entity using Datamapper:
I want to keep track of compatibility of various software releases I'm tracking:
class SoftwareRelease
include DataMapper::Resource
property :id, Serial
property :software_release_type_id, Integer
property :version, String
belongs_to :software_release_type
has n, :compatibilities, child_key: [ :source_id ]
has n, :compatible_releases, self, through: :compatibilities, via: :target
end
class SoftwareReleaseType
include DataMapper::Resource
property :id, Serial
property :name, String
property :short_name, String
has n, :software_releases
end
class Compatibility
include DataMapper::Resource
belongs_to :source, 'SoftwareRelease', key: true
belongs_to :target, 'SoftwareRelease', key: true
end
A given software release 'a.b.c' can have a type of 'hardware platfrom foo', while a given software release 'd.e.f' can have a type of 'hardware platform bar'.
To establish compatibility between the software release in the above code, I must perform 2 adds
release_a_b_c.compatible_releases << release_d_e_f
release_d_e_f.compatible_releases << release_a_b_c
That's kinda ugly.
I'd love to be able to do just one add and have a symmetric relationship established. I can obviously wrap 2 pushes with an instance method in SoftwareRelease, but that feels like I'm sweeping the ugliness under a rug. Any ideas for an elegant solution?
Much obliged,
Pawel
PS
Long time ago, active-record had acts_as_network plugin which did something similar. Haven't been able to find anything in the DM world.
I don't think there is anything in DM or any of its plugins to do what you want, but I would like to posit that writing an instance method to create both one-way relations is in fact fairly elegant and I wouldn't consider it "sweeping the ugliness under the rug" at all.
Part of the power of implementing object relations in classes is that the programmer gets to extend those classes in meaningful ways by creating instance methods inside the class. You have a need for a symmetrical relationship between objects, so writing that simple instance method is meaningful and self-documenting. And there is no risk of side effects or inappropriate calls because it is a public method of that specific class.
I'm not really saying anything new, here, but I think your solution is perfectly acceptable. Frankly, I would rather see relatively rare use cases pushed into the programmer's domain, rather than clutter up the DataMapper code. I don't need one of those big fat Victorinox pocket knives with every conceivable tool. :)

M2M relationship or 2 FKs?

Which of the following structures would be preferable:
# M2M
class UserProfile(models.Model):
...
groups = models.ManyToManyField(Group)
class Group(models.Model):
...
or -
# 2 FKs
class UserProfile(models.Model):
...
class Group(models.Models):
...
class GroupMember(models.Model):
user = models.ForeignKey(UserProfile)
group = models.ForeignKey(Group)
Which would be better?
You also can combine these 2 variants using through option
groups = models.ManyToManyField(Group, through='GroupMember')
What do you mean by better? Usually you don't need to create intermediate model (except the case when you have to store extra data).
ManyToManyField does his job perfectly, so don't write its functionality by yourself.
The two are essentially the same. When you do a M2M Django automatically creates a intermediary model, which is pretty much exactly like your GroupMember model. However, it also sets up some API hooks allowing you to access the Group model directly from the UserProfile model, without have to mess with the intermediary model.
You can get the same hooks added back by using through as #San4ez explains, but you've only made things more complicated. Creating a custom through model is only beneficial if you need to add additional fields to the relationship. Otherwise, stick with the default.
Long and short, #1 is better, only because it's exactly the same as #2, but simpler and with no extraneous code.

Create separate classes for insert and save

Is this a good idea? Instead of create a class with two method (insert and update) and two validation methods (validateInsert and validateUpdate), create three classes: one called ProductDB, another ProductInsert (with methods Insert and Validate) and another ProductUpdate (with same methods of ProductInsert).
Is this more readable, flexible and testable?
PaulG's answer leans more towards the traditional domain object pattern, which I'm not in favor of. Personally, my preference is to have a separate class for each process (like your ProductInsert and ProductUpdate). This is akin to what one sees in the simple bank example where Deposit is a instance of a class as opposed to a method on a BankAccount class. When you start thinking about business processes that have more stuff, like rules and actions to be taken and auditing/persistence of the action itself (say a ProductInsert table to track insertions), the more you realize the business process should be a first class citizen in its own right.
This sounds like a language-independent question. I would just create the one class and call it Product, and have the appropriate methods within the class. Think about what a mess it would be when actually instantiating your separate objects (unless you have static methods).
Also having a concrete Product class will allow you to store object specific information.
Ex:
Product myProduct = new Product()
myProduct.name = "cinnamon toast crunch"
myProduct.price = 3.99
In my opinion have separate classes would make your code a lot less readable and testable.

Polymorphic belongsTo in many to many mapping in grails?

So i know this is possible using a superclass, however, this is very limiting in flexibility. So my question is then, can i use an interface? Something ala.
interface Taggable {
/*Adds tag(s) and returns a list of currently set tags*/
List<String> addTags(String ... tag)
/*Removes tag(s) and returns a list of currently set tags*/
List<String> removeTags(String ... tag)
}
class User implements Taggable {
String username
static hasMany = [tags:Tag]
}
class Tag {
String name
static hasMany = [references:Taggable]
static belongsTo = Taggable
static constraints = {
name(nullable: false, blank: false, unique: true)
}
}
Im interested in a reference back to the object who has the following tag. This object however can't extend a concrete class. Thats why im wondering if this can be done with an interface instead.
So, can it be done?
Hibernate can map an interface - see example. I doubt if Grails supports this in by-convention mapping - but you can try using the mapping annotations from example above, or XML config.
edit: answering a comment question:
On a database level, you have to have a Taggable table for Tag.References to reference with a foreign key.
Discriminator will NOT defeat polymorphism, if it's added automatically - for instance, in table-per-hierarchy mapping, Hibernate/Gorm adds a class field in order to find out a concrete class when reading object from db.
If you map your Taggables to two tables - Taggable part to Taggable and everything else to specific table, referenced 1:1 - all the discriminator work should be done for you by Hibernate.
BTW class field is pretty long - it contains fully qualified class name.
edit 2:
Either way, it's getting pretty complex, and I'd personally go with the approach I suggested in another question:
dynamically query all the classes with Taggable interface for hasMany=[tags:Tag] property;
or, less preferable - to have a hand-crafted child table and a discriminator.