Search code examples
ruby-on-railsneo4j.rb

Neo4jrb: Enumerator for model_class: false


When specifying a model_class of false, it's expected that the connection from this node to another could be any other class of node.

Is there a reciprocal enumerator so that we can call something like:

ActiveNodes.all

when giving the user a selection of nodes to choose from?

Note (and warning to anyone thinking about this): Looking for all nodes would not scale at all.


Solution

  • If you've got a good use case we would certainly consider it something to make this easier. The problem is, though, there would be no associations to browse at that point because there's no model involved. You might just want to use the Query API at that point:

    Neo4j::Session.current.query.match(:n).where(n: {foo: 'bar'}).etc...
    

    The documentation for all of the methods is here:

    http://neo4jrb.readthedocs.io/en/7.0.x/QueryClauseMethods.html

    EDIT:

    Editing the post based on your comment because there isn't enough room in a comment ;)

    Your example is that you have a Product and you would like to be able to find other models which are linked with the same relationship type. Here's what I might do:

    class Product
      include Neo4j::ActiveNode
    
      has_one :out, :file, type: :RELATED_FILE, model_class: %i(ProductImage ProductVideo ProductPDF)
    
     # optionally
     has_one :out, :image_file, type: :RELATED_FILE, model_class: :ProductImage
     has_one :out, :video_file, type: :RELATED_FILE, model_class: :ProductVideo
     has_one :out, :pdf_file, type: :RELATED_FILE, model_class: :ProductPDF
    end
    

    These could also be changed to has_many and work just the same if there's the possibility of having more than one associated.

    Then you should be able to generate a select tag in the view which has all of the options and which will pass the ID(s) down to the controller via params and then you could just do:

    Product.create(name: 'or whatever props you have', file_id: params[:file_id])
    

    If the association is has_many you could instead do file_ids: params[:file_ids].

    You might also consider inheriting those associated models from a single class like this:

    class File
      include Neo4j::ActiveNode
    end
    
    class ProductImage < File
      # No need to `include Neo4j::ActiveNode`
    end
    

    Then you could do the array of models like I suggested above, or you could simply do:

    class Product
      include Neo4j::ActiveNode
    
      has_one :out, :file, type: :RELATED_FILE
    end
    

    model_class: :File is assumed based on the association's name. If you had has_many then your association name would be :files and it will still assume model_class: :File.

    Just be aware that if you do this, nodes for your associated models (ProductImage, ProductVideo, and ProductPDF) would all also have the File label because of the inheritance.

    As a separate modeling suggestion, I would probably just have an Image model (for example) rather than ProductImage so that the model could be potentially re-used.