neo4jrb / activegraph

An active model wrapper for the Neo4j Graph Database for Ruby.
http://neo4jrb.io
MIT License
1.4k stars 277 forks source link

Chain associations / class methods on QueryProxy #380

Closed cheerfulstoic closed 10 years ago

cheerfulstoic commented 10 years ago

Check out the query_experiment branch. For good examples see the query_spec:

https://github.com/andreasronge/neo4j/blob/query_experiment/spec/e2e/query_spec.rb

The code is a bit hairy right now, but I'm really excited about this approach. It takes over some of the conceptual ground of QuickQuery without the .qq method, it introduces a more ActiveRecordy has_many association, and it allows for calling class methods on QueryProxy objects (which are also now returned from has_many associations)

This is aping a lot of ActiveRecord, but I think it's taking it a step beyond what ActiveRecord is capable of doing. I'm really excited about it ;)

Some things that would need to be done:

andreasronge commented 10 years ago

Chris, how about you and me old dinosaurs use the rels. Method, we support both way, need to think more and see more examples On 24 Jul 2014 21:19, "Chris Grigg" notifications@github.com wrote:

Err... I sent that from my phone and then edited without realizing that you want to still use has_(many|one) but change the meaning. I'm not so sure about that... but I won't object if you guys outvote me. I can adapt, I just like the directness of defining direction with two master methods. Feels like less of a learning curve, too.

— Reply to this email directly or view it on GitHub https://github.com/andreasronge/neo4j/issues/380#issuecomment-50064937.

subvertallchris commented 10 years ago

Since Brian agreed to back off for now, let's leave it alone and let this die! Haha. But Brian, if you find that you have a lot of success with belongs_to/etc,... maybe it'll be worth revisiting? Or just doing it in a separate gem, neo4j-belongs_to or something?

subvertallchris commented 10 years ago

Just got this working in the conversion branch of my app, works great. This is the most nitpicky suggestion I think I've ever had, but do you guys think that :outgoing/:incoming might work as better keywords than :outbound/:inbound? I'm sorry, I'm sorry, I want this closed as much as you, it was just something I noticed while typing it up. :-/

Overall, though, I'm really happy with it. It's immediately clear what is happening at all times with each association. When origin is added in, it'll be even better; when ActiveRel (or whatever its final name is), it'll be really great.

Can you add in support for the before/after callbacks I added a week or two ago? I think @sahin is using them and since he was the first to bring up renaming relationship types, it would be a shame to leave that behind.

subvertallchris commented 10 years ago

Also, Brian, awesome work getting this (and all earlier variations) in there!!!

cheerfulstoic commented 10 years ago

Thanks (and thanks for all the feedback ;)

I was just going to implement origin, but I have a question:

class Show
  has_many :outgoing, :bands
end

class Band
  has_many :incoming, :shows, origin: :bands
end

What if that :incoming were :outgoing instead? Should :origin just mean that we create an appropriate relationship type prefixed with # or should we bother verifying how it links up to the other relationship?

For now I'm just going to have it make the relationship (and maybe validate that you can't have both type and origin at the same time)

subvertallchris commented 10 years ago

So you mean, what if they're both set to :outgoing? I think it should only be concerned with the relationship type, unless it's simple enough to add that check that you feel like doing it. They'll post an issue and we'll politely tell them to read the docs more carefully. ;-) (We'll probably point this out a dozen times in the docs since it's such an important guideline.)

But I think it should just verify that the corresponding method exists and that it has a type specified or magically created. If it can't find the association, it fails; if they both have :origin set, they both fail. Seem reasonable?

andreasronge commented 10 years ago

I had a look at the specs query_spec.rb in your branch and it looks very good.

I also prefer outgoing and incoming. A detail, I guess your specs will run slowly since you create the node space in an before(:each) block instead of before(:all)

cheerfulstoic commented 10 years ago

If we're checking that the method exists on the other side it's probably pretty simple to check that they're not both going in the same direction. I'll add that to avoid the questions ;)

I think I actually prefer inbound and outbound, but it's not a big preference. What about in, out, and both?

And yeah, the spec will run slower because of that. Honestly there's probably room to be using before(:all) in some cases. I'm also thinking that I'm going to be splitting up the spec shortly because it's becoming hard to remember what all is setup. Will probably split out the association chanining stuff and then the has_(one|many) stuff will probably need to be organized and tested on a feature-by-feature basis.

subvertallchris commented 10 years ago

Unless Andreas feels reeeaally strongly about it, I propose we leave inbound/outbound alone for now and we can discuss it when we're not sending messages back and forth. It would be a 5 minute conversation or half a dozen comments to this issue over a 12 hour period! Haha.

andreasronge commented 10 years ago

:) Sorry just an another comment Regarding testing I think we should write more unit test and mock more since it will give much faster tests. I like to mix many small unit tests with a few end to end tests. I would also like to clean up my own specs.

Regarding inbound and outbound (sorry): I would prefer in, out, and both. If you change I would like to change in the neo4j-core as well.

cheerfulstoic commented 10 years ago

Good to hear since I'm in the process of doing exactly that (regarding unit testing).

And yay, somebody agrees with me (regarding in/out/both)! ;) @subvertallchris Objections?

subvertallchris commented 10 years ago

No objects! Full support! This is awesome! Go team!

andreasronge commented 10 years ago

Great discussion ! Feels like we come up with a really awesome new API. Looking forward seeing the PR.

cheerfulstoic commented 10 years ago

FYI I just pushed some changes to support in/out/both and origin. I've also refactored the specs to be easier to read and made unit specs for the Association class. I just found out that a lot of other specs are failing, but I've got to go to bed now, so I'll look at that in the morning. Next up after that is implementing the association callbacks ( @subvertallchris I know you're waiting on this to be working, so I'm happy for a hand on either point).

cheerfulstoic commented 10 years ago

Just pushed some changes and it's getting closer. Have one question for @andreasronge though. I've been trying to convert the integration specs, but the doubles are multiplying. It comes down to the fact that the relationship creation/querying isn't using neo4j-core API methods like #rels, #nodes, and #create_rel but rather using the core Query API. Does this seem like a good path to you? Do integration tests make as much since in this context? I'm somewhat experienced with mocks/stubs/etc... but I don't know that I'm comfortable knowing when they're appropriate.

andreasronge commented 10 years ago

I think it is important that the specs are simple and easy to understand. If there are too many stubs and mocks then I think either the code is too complex or the RSpecs tests are too coarse grained. I would also like to reorganize the existing rspecs (some of them are from the 2.x version)

andreasronge commented 10 years ago

Btw, I think we later on should measure the performance difference of using #rels and #nodes instead of the query api when using the embedded neo4j

sahin commented 10 years ago

It is just an amazing progress.

Can someone summarize the latest syntax?

is it? has_many :outgoing, :bands

and in the neo4j db, what will be name of the relation Show#outgoing#bands or just bands

subvertallchris commented 10 years ago

It's all described at https://github.com/andreasronge/neo4j/wiki/Neo4j-v3-Declared-Relationships but here are some basics.

Automatically named relationships are prefixed with #, use the rel_type option with a string to set the type. There's also an :origin option for reciprocal relationships or you can use ActiveRel to separate relationship logic.

#look for class Band, rel type is '#bands'
has_many :out, :bands

#look for class Band, rel_type is 'has_bands'
has_many :out, :bands, rel_type: 'has_bands'

#look for class Band, association is :people_playing_instruments, rel_type is 'has_bands'
has_many :out, :people_playing_instruments, model_class: Band, rel_type: 'has_bands'

#look for any class
has_many :out, :things, model_class: false, rel_type: 'has_things'
sahin commented 10 years ago

Thx, They look good. There was 100s comments. I totally skip the wiki. :) We will move to this branch and do some testing.

I am sure you all discuss it, I think it might be a very simple syntax for activemodel, mongoid users to make has_many ":outgoing," as default. to -> has_many :bands

if they want more control "in" or declare out then devs can write simply has_many :outgoing, :bands

subvertallchris commented 10 years ago

EDIT: There, fixed my example. It's :out, :in, :both, not :outgoing, :incoming. Had my wires crossed cause of your question. :-)

subvertallchris commented 10 years ago

We actually did talk about having outbound as the default direction but I think we settled on requiring a direction first to keep things clear, explicit, and help the user be more aware of the fact that direction is a crucial part of an association.

This is in master now, no need to switch to a different branch. It was included with alpha 9.

sahin commented 10 years ago

cool. thx...

sahin commented 10 years ago

A dummy question.

I am trying to create the relations with new style. I couldnt make it work.

   class NeoPerson
       include Neo4j::ActiveNode
      set_mapped_label_name('Person')
      property :mongo_id; validates :mongo_id, uniqueness: true,  presence: true
      property :name,  presence: true

      has_many :in, :followed_by, model_class: NeoUser, type: 'followed_by'
  end

 class NeoUser
     include Neo4j::ActiveNode

     set_mapped_label_name('User')

     property :mongo_id
     validates :mongo_id, uniqueness: true,  presence: true
     index :mongo_id

     property :name,  presence: true
     property :facebook_id
     validates :facebook_id, uniqueness: true
     index :facebook_id

     has_many :out, :follow_person, model_class: NeoPerson, type: 'follow_person'
 end

The code I was running is :

user = NeoUser.first person = Person.first.neo user.follow_person << person user.save! user.follow_person.to_a

in db, I cannot see the relation or in to_a, it is []

subvertallchris commented 10 years ago

Your type doesn't match on the two models, is that deliberate? That wouldn't account for your issue, just pointing it out.

I wonder if using set_mapped_label_name is causing any problems, I don't know how that affects things. What happens if you do it with person = NeoPerson.first?

sahin commented 10 years ago

NeoPerson.first and NeoUser.first, they both working and I am getting

<NeoPerson mongo_id: "52f9500d776f72e1697a0000", name: "Alexander Payne">

let me check with out without set_mapped_label_name

sahin commented 10 years ago

bad news, it works perfectly without set_mapped_label_name.

so set_mapped_label_name is creating the problems.

user.follow_person.to_a
[#<NeoPerson mongo_id: "52f9500d776f72e1697a0000", name: "Alexander Payne">]
sahin commented 10 years ago

I fixed the type still same, so it is causing because of set_mapped_label_name

subvertallchris commented 10 years ago

Yeah, it's the mapped label names for sure. When the association, it is making a cypher statement based on the class name, not its label name. It might be a quick fix, give me just a minute.

subvertallchris commented 10 years ago

I can fix the relationship creation but querying for associations is a lot more complicated, @cheerfulstoic built it so he might know an easy fix.

sahin commented 10 years ago

cool, let me check

subvertallchris commented 10 years ago

Don't get too excited, I haven't pushed anything yet. ;-)

sahin commented 10 years ago

:)

subvertallchris commented 10 years ago

@sahin, let's move this over to #420.