Closed daneshk closed 4 months ago
The unique indexes
and unique constraints
are two different elements to consider when we use relational databases.
Unique Indexes
- Used to improve query performance by allowing for faster lookups on certain columns or fields. These indexes enforce a uniqueness restriction on certain fields so that all values stored within those fields must be unique from other values in the same column. This means that if you search for an entry within the indexed field, it will quickly retrieve the exact value stored within it instead of having to perform a full table scan.
Unique Constraints
- Used to enforce data integrity by ensuring that no duplicate values can be inserted into a database. Unlike Unique Indexes, Unique Constraints don’t just index the column – they prevent duplicate values from being entered into a table altogether.
If you want better performance for queries then using a Unique Index would be best since it can help reduce processing time by providing quicker lookups in specified fields; however, if your main priority is data integrity then opting for a Unique Constraint would be best since it prevents any potential data inconsistency issues by making sure only valid entries can be added.
Check different other implementations,
Prisma doesn't differentiate unique indexes vs unique constraints. AFAIU, they decides whether to create indexes or constraints based on the DB https://www.prisma.io/docs/orm/reference/prisma-schema-reference#unique
https://www.baeldung.com/jpa-unique-constraints https://www.baeldung.com/jpa-indexes
https://learn.microsoft.com/en-us/ef/core/modeling/indexes?tabs=data-annotations
index
, uniqueIndex
gorm:"index:idx_name,unique"
, gorm:"uniqueIndex"
- creates unique constraintgorm:"index"
- creates indexgorm:"index:idx_member"
- Use the same index name to define composite indexesgorm:"index:idx_id;index:idx_oid,unique"
attaches multiple indexes to the same fieldDesign consideration
index
or unique index
. Not very clear about unique index
vs unique constraint
. Though there are syntax differences in defining them, both theoretically do the same thing.Proposed Design
Based on the above, the initial proposed solution as follows,
In the persist.sql
model, we introduce new annotations called Index and Unique Index like below,
# Marks the entity field as an index field.
public type IndexConfig record {|
# The name used to define the index. This is required to group the fields for the composite index
# If the field is mapped to two or more composite index groups, index names are listed in an array.
string|string[]? name = ();
|};
# The Annotation indicates the index field in the `bal persist` model type.
public annotation IndexConfig Index on record field;
# The Annotation indicates the unique index field in the `bal persist` model type.
public annotation IndexConfig UniqueIndex on record field;
So developer can give the mapping DB table name and column names for the record and record field using Map annotation in the data model definition as follows,
import ballerina/persist as _;
import ballerinax/persist.sql;
type Doctor record {| readonly int id; @sql:Index string name; string specialty; @sql:UniqueIndex string phoneNumber; Appointment[] appointment; |};
* For composite index and unique index fields
```ballerina
import ballerina/persist as _;
import ballerinax/persist.sql;
# As the index names of both `name` and `specialty` and the same, they form a composite index.
type Doctor record {|
readonly int id;
@sql:Index("idx_id")
string name;
@sql:Index("idx_id")
string specialty;
string phoneNumber;
Appointment[] appointment;
|};
import ballerina/persist as _;
import ballerinax/persist.sql;
type Doctor record {| readonly int id; @sql:Index(["idx_specialty", "idx_name"]) string name; @sql:Index("idx_specialty") string specialty; @sql:Index("idx_name") string phoneNumber; Appointment[] appointment; |};
IMO, the ideal solution is this.
# Here the name field is included in two different composite indexes.
type Doctor record {|
readonly int id;
@sql:Index {name: "idx_specialty" }
@sql:Index {name: "idx_name" }
string name;
@sql:Index {name: "idx_specialty"}
string specialty;
@sql:Index {name: "idx_name"}
string phoneNumber;
Appointment[] appointment;
|};
But, you cannot declare the same annotation twice for a single attachment point. On the other hand, a table can have one or more indexes and each index has a name. With that definition, I believe the following should work. I prefer name
instead of names
.
# Here the name field is included in two different composite indexes.
type Doctor record {|
readonly int id;
@sql:Index {name: ["idx_name", "idx_specialty"] }
string name;
@sql:Index {name: "idx_specialty"}
string specialty;
@sql:Index {name: "idx_name"}
string phoneNumber;
Appointment[] appointment;
|};
This feature is implemented. Hence closing the issue
Summary
The current
bal persist
design doesn't support making entity field(s) unique keys and composite unique keys. So there is no way to mark entity fields as unique keys. We need to support this for the DB introspection.