Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lamindb/models/sqlrecord.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,18 @@ def suggest_records_with_similar_names(
else:
subset = record.__class__
exact_match = subset.filter(**{name_field: kwargs[name_field]}).first()
# Fallback: if no match was found and no type= was passed, also search for
# is_type=True records that have a non-null parent type. Such records (e.g. a

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why is_type=True is missed above. Can you explain?

# Project subtype) are missed by the filter(type__isnull=True) above and would

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you 100% sure about this? I'm not seeing why it would be missed.

# otherwise produce a silent duplicate on .save().
if (
exact_match is None
and isinstance(record, HasType)
and kwargs.get("type", None) is None
):
exact_match = record.__class__.filter(
is_type=True, **{name_field: kwargs[name_field]}
).first()
if exact_match is not None:
return exact_match
queryset = _search(
Expand Down
33 changes: 33 additions & 0 deletions tests/pydata/test_sqlrecord.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,39 @@ def test_suggest_similar_names():
record4.delete(permanent=True)


def test_dedup_project_with_parent_type():
"""Dedup must find a project that has is_type=True AND a non-null parent type.

Before the fix, suggest_records_with_similar_names used filter(type__isnull=True)
when no type= kwarg was passed. This excluded any project whose type_id is not
null, so a duplicate was silently created instead of returning the existing record.
"""
parent = ln.Project(name="__test_dedup_parent__", is_type=True).save()
original = ln.Project(
name="__test_dedup_subtype__", is_type=True, type=parent
).save()
assert original.type_id is not None # confirm this is the case the old filter missed

# caller knows it's a type but does not pass type=
returned1 = ln.Project(name="__test_dedup_subtype__", is_type=True).save()
assert returned1.uid == original.uid, (
f"Dedup should have returned uid={original.uid}, got uid={returned1.uid} (duplicate)"
)

# caller has no idea it's a type at all — Shoh's exact scenario
returned2 = ln.Project(name="__test_dedup_subtype__").save()
assert returned2.uid == original.uid, (
f"Dedup should have returned uid={original.uid}, got uid={returned2.uid} (duplicate)"
)

assert ln.Project.filter(name="__test_dedup_subtype__").count() == 1, (
"A duplicate project was created — dedup missed the original."
)

original.delete(permanent=True)
parent.delete(permanent=True)


def test_pass_version():
# creating a new transform on key retrieves the same transform
# for as long as no source_code was saved
Expand Down
Loading