secretworry / as_nested_set Goto Github PK
View Code? Open in Web Editor NEWa ecto based nested set implementation for database
License: MIT License
a ecto based nested set implementation for database
License: MIT License
Hi, I'm having troubles with adding a record as a child in tests:
defmodule SomeApp.Api.Node do
use Ecto.Schema
use AsNestedSet, scope: [:emotion_id]
end
root_node = %Node{emotion_id: emotion.id, name: "root"} |> AsNestedSet.create(:root) |> AsNestedSet.execute(Repo)
child_node = %Node{emotion_id: emotion.id, name: "asd"} |> AsNestedSet.create(root_node, :child) |> AsNestedSet.execute(Repo)
IO.inspect root_node
IO.inspect child_node
# root node, works fine, lft and rgt are calculated
%SomeApp.Api.Node{
__meta__: #Ecto.Schema.Metadata<:loaded, "nodes">,
emotion_id: 65,
id: 96,
inserted_at: ~N[2022-03-31 09:10:34],
lft: 0,
name: "root",
parent_id: nil,
rgt: 1,
updated_at: ~N[2022-03-31 09:10:34]
}
# supposed to be a child of root node, but it's not
%SomeApp.Api.Node{
__meta__: #Ecto.Schema.Metadata<:loaded, "nodes">,
emotion_id: 65,
id: 97,
inserted_at: ~N[2022-03-31 09:10:34],
lft: nil,
parent_id: nil,
rgt: nil,
updated_at: ~N[2022-03-31 09:10:34]
}
Am I missing something? Seemingly two identical ways of creating an entity in the tree behave differently. If I create a child node as another root first and move it, it works, but creating it as a child right away does not seem to be working.
Elixir 1.13.2
as_nested_set 3.4.1
ecto 3.7.2
phoenix 1.6.6
I may be misreading the documentation, but I don't understand why scope is required. I can see the use case for having it as an option, but in our case we have a table where the entire table is a single nested set, so I don't need a taxonomy id, I would think.
Thanks!
Can I have it wait to calculate the nested set, so I can do a set of inserts/moves, and then tell it to calculate afterward?
** (ArgumentError) comparison with nil is forbidden as it is unsafe. If you want to check if a value is nil, use is_nil/1 instead
(ecto 3.4.4) lib/ecto/query/builder.ex:907: Ecto.Query.Builder.not_nil!/1
(as_nested_set 3.3.0) lib/as_nested_set/scoped.ex:55: anonymous fn/3 in AsNestedSet.Scoped.do_scoped_query/3
(elixir 1.10.2) lib/enum.ex:2111: Enum."-reduce/3-lists^foldl/2-0-"/3
(as_nested_set 3.3.0) lib/as_nested_set/modifiable.ex:199: AsNestedSet.Modifiable.do_reload/2
(as_nested_set 3.3.0) lib/as_nested_set/modifiable.ex:12: anonymous fn/4 in AsNestedSet.Modifiable.create/3
I don't know that this is required in the deps anymore; and it makes it so the app is not available when running as a release. I have removed it, and it works fine.
I may be mis-understanding what is used for this attribute. I am setting it to a different column name (upline_id, instead of the default parent_id). But this attribute is never set (in the DB it's always NULL).
I was interpreting things such that the id for the target sent into .create() would be included on the child's row as the parent_id. I see in the code where @parent_id_column is referenced, I'm just not certain why all my values end up being NULL/nil.
Any help is greatly appreciated. Thanks!
Earlier I submitted a problem with parent_id not mapping, I think the heart of the problem I'm having is move doesn't appear to be working as I would expect (I would assume it behaves similar to create, in how target
is the parent for the node, but it seems to be taking the parent_id of the target itself).
I'm probably just not understanding or I'm using it incorrectly, but I'd appreciate some insights. A simplified example of what I'm doing is receiving a stream of tree changes, that come in with a node_id and parent_id. The module/struct for the example is %Tree
.
My code queries the DB to find if the node_id already exists, and if it does it calls :create
or :move
appropriately.
In my tests, I do the following sequence of changes:
1. %{parent_id: nil, node_id: 100, position: :left}
2. %{parent_id: 100, node_id: 101, position: :left}
3. %{parent_id: 100, node_id: 102, position: :right}
4. %{parent_id: 102, node_id: 103, position: :left}
5. %{parent_id: 102, node_id: 104, position: :right}
6. %{parent_id: 101, node_id: 103, position: :left}
7. %{parent_id: 101, node_id: 103, position: :left}
The problem I'm running into starts at #6, where I submit the request to move node 103 to 101 (pre-existing state at this point is it has a parent of 102). The result that comes back from .execute() does not have the parent ID changed. Test #7 should then be returning "duplicate" as the change can be skipped--but it too is failing because it isn't a duplicate, and it tries to do the move again, also failing.
The more involved output:
Request Struct:
node = %Tree{__meta__: #Ecto.Schema.Metadata<:built, "tree">, id: nil, level: 1, lft: nil, position: 0, rgt: nil, node_id: 103, parent_id: 101}
Current Database Result (before change):
lookup = %Tree{__meta__: #Ecto.Schema.Metadata<:loaded, "tree">, id: 437, level: 1, lft: 4, position: 0, rgt: 5, node_id: 103, parent_id: 102}
Lookup Desired parent in DB:
parent = %Tree{__meta__: #Ecto.Schema.Metadata<:loaded, "tree">, id: 435, level: 1, lft: 0, position: 0, rgt: 1, node_id: 101, parent_id: 100}
Commit:
node
|> AsNestedSet.move(parent, :left)
|> AsNestedSet.execute(Repo)
|> IO.inspect
Result:
%Tree{__meta__: #Ecto.Schema.Metadata<:loaded, "tree">, id: 437, level: 1, lft: 0, position: 0, rgt: 1, node_id: 103, parent_id: 100}
What is really throwing me off here is that the parent_id is 100
. This might be a mis-understanding of the docs, but from the shown examples I'd assume that target
is the parent node. This is how it seems to work when I do this with create
, but with move
it seems to be taking the parent.parent_id
, instead of parent.node_id
Any suggestion/help/insight you can provide is greatly appreciated.
Thanks!
Hi,
First of all nice work on building this library, the api you expose to consumers looks awesome !
I had a look at the do_safe_create
in AsNestedSet.Modifiable
and i was wondering why the 3 database operations are not in a transaction ?
If one of the operations fails would that not cause issues with data consistency of the tree ?
best,
Stefan
So we won't pollute the target model. Instead, we just create a __as_nested_set__
methods for targeting model
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.