Reputation: 11409
I will give a quick background. I am building a directed graph in Rails with the following (summarized) models:
Node
has_many :edges, foreign_key: "source_id"
and
Edge
field :source_id, :type => String
field :destination_id, :type => String
belongs_to :source, class_name: "Node"
belongs_to :destination, class_name: "Node"
I have come across 2 bizarre issues with Mongoid has_many and belongs_to relationship. The results of the relational query seem to vary depending on what method I use to retrieve the Node
object.
First, calling to_a
on the relation (as well as enumerating over the relation, i.e. each
, map
, collect
) causes an extra Edge
to be retrieved, as seen below where the count returns as 31.
Second, the issue with the extra Edge
only seems to occur when the query that retrieves the Node
is different than a straight forward find
. But as you can see, according to Rails node1
is equal to node2
.
I would greatly appreciate if anyone can shed any light on these issues. Let me know if additional information would be helpful.
1.9.3-p327 :145 > node1 = some_other_node.neighbors.select {|n| n[:node].city == "983"}.first[:node]
=> #<Node _id: 54da32b1756275343ed70300, city: "983">
1.9.3-p327 :140 > node2 = Node.find_by(:city => "983")
=> #<Node _id: 54da32b1756275343ed70300, city: "983">
1.9.3-p327 :141 > node1 == node2
=> true
1.9.3-p327 :150 > node1.edges.count
=> 30
1.9.3-p327 :151 > node1.edges.to_a.count
=> 31
1.9.3-p327 :152 > node2.edges.count
=> 30
1.9.3-p327 :153 > node2.edges.to_a.count
=> 30
EDIT Providing information about the extra edge.
The extra Edge
that is returned should absolutely not be returned. You can see below that the extra Edge
has a source
that is not equal to node1
. Every other Edge
that is returned has node1
as the source, which is precisely what I expect based on the relationship.
1.9.3-p327 :167 > node1.edges.to_a.last.source
=> #<Node _id: 54da32b0756275343e000000, city: "0">
Here is more evidence to support my claim. Notice that all of node2
edges have node2
as the source
, but this is not true for node1
.
1.9.3-p327 :170 > node2.edges.to_a.collect {|e| e.source == node2}.include? false
=> false
1.9.3-p327 :171 > node1.edges.to_a.collect {|e| e.source == node1}.include? false
=> true
However, it is peculiar that the extra Edge
would have city: "0"
as the source because in the initial assignment of node1
the some_other_node
is city: "0"
. That does not seem like a coincidence.
Upvotes: 1
Views: 191
Reputation: 11409
Issue resolved! Thanks for the suggestion mu is too short. When I attempted to implement both out_edges
and in_edges
on the Node
I discovered that Mongoid was complaining about the inverse relation being ambiguous. Interestingly, it was happily silent when I was only defining the out_edges
.
Just to clarify, It is not required to define both out_edges
and in_edges
, I was able to make the defect go away by including the inverse_of
definitions in the Edge
model.
Node
has_many :edges, foreign_key: "source_id"
Edge
field :source_id, :type => String
field :destination_id, :type => String
belongs_to :source, inverse_of: "edges", class_name: "Node"
belongs_to :destination, inverse_of: "in_edges", class_name: "Node"
EDIT
Rails/Mongoid do not appear to care that destination
is defined with inverse_of: "in_edges"
even though in_edges
does not exist in Node. But if I remove that part then the issue returns. Mongoid definitely has some defects here.
Upvotes: 1