Repo.set creates new node rather than updating existing node
Expected behaviour: Existing node should be updated
Actual behaviour: New node is created every time
version: 0.5.1
Same implementation was working fine with earlier version.
Implementation:
file lib/lms/courses/course.ex contains:
defmodule Lms.Courses.Course do
use Dlex.Node
@derive {Phoenix.Param, key: :uid}
import Ecto.Changeset
schema "courses" do
field(:description, :string)
field(:duration_allowed, :integer)
field(:duration_units, :integer, default: 3)
field(:position, :integer)
field(:status, :integer)
field(:title, :string, index: ["term"])
field(:guidelines, :string)
field(:guidelines_title, :string)
field(:has_learning_path, :uid)
field(:created_at, :datetime)
field(:updated_at, :datetime)
end
@doc false
def changeset(course, attrs) do
course
|> cast(attrs, [
:title,
:description,
:duration_allowed,
:duration_units,
:status,
:position,
:guidelines,
:guidelines_title
])
end
end
and lib/lms/courses.ex contains:
def update_course(%Course{} = course, attrs) do
course
|> Course.changeset(attrs)
|> Repo.set()
end
Following creates a new node rather than updating the existing node:
id = "0x7533" # its a valid id and returns Course struct
course = Repo.get!(id)
course_params = [
attrs: %{
"description" => "",
"duration_allowed" => "",
"guidelines" => "",
"guidelines_title" => "",
"position" => "",
"status" => "1",
"title" => "updated title"
}
]
Lms.Courses.update_course(course, course_params)
Course.changeset(attrs) produces:
changeset: #Ecto.Changeset<
action: nil,
changes: %{status: 1, title: "updated title"},
errors: [],
data: #Lms.Courses.Course<>,
valid?: true
>
and data key contains:
%Lms.Courses.Course{
created_at: nil,
description: nil,
duration_allowed: nil,
duration_units: 3,
guidelines: nil,
guidelines_title: nil,
has_learning_path: nil,
position: nil,
status: 1,
title: "now we got it",
uid: "0x7533",
updated_at: nil
}
Tried replacing Repo.set with Repo.mutate but same issue persists.
When updating, are you casting :uid as a field?
I use
struct
|> cast(params, [
:uid,
:title
])
|> validate_required([:uid])
and it works.
To those who are looking for a solution, i.e. to update existing nodes in Dgraph with Ecto changesets:
- Cast
uidin your changeset. - Validate require
uidin your changeset. - Force change
uidin your changeset.
For example:
def update_changeset(%Struct{} = struct, attrs) do
struct
|> cast(attrs, [:uid])
|> validate_required([:uid])
|> force_change(:uid, struct.uid)
end
This will update an existing node in Dgraph based on the given uid.
Perhaps it's possible to indicate to Ecto that uid is a primary key so that uid is automatically added to Ecto.Changeset changes field, but I have not tested this.
Thanks Martin for the solution. I had dropped the implementation, will try to pick it up again.