Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unique indexes and indexes for composite types #7162

Merged
merged 43 commits into from
Oct 13, 2024

Conversation

FelixMalfait
Copy link
Member

@FelixMalfait FelixMalfait commented Sep 19, 2024

Add support for indexes on composite fields and unicity constraint on indexes

This pull request includes several changes across multiple files to improve error handling, enforce unique constraints, and update database migrations. The most important changes include updating error messages for snack bars, adding a new command to enforce unique constraints, and updating database migrations to include new fields and constraints.

Error Handling Improvements:

New Command for Unique Constraints:

Database Migrations:

GraphQL Exception Handling:

@FelixMalfait FelixMalfait changed the title Add unique indexes Add unique indexes and indexes for composite types Sep 19, 2024
@Weiko Weiko assigned Weiko and unassigned Weiko Oct 3, 2024
@FelixMalfait
Copy link
Member Author

FelixMalfait commented Oct 6, 2024

Todo:

  • check that having 2 null-s isn't considered as a unicity constraint violation
  • update GraphqlQueryUpdateManyResolverService.validate, GraphqlQueryUpdateOneResolverService.validate and
    GraphqlQueryCreateManyResolverService.deleteMany to perform a pre-check there and throw a user-friendly unicity violation error at application level and not wait for the error to happen at the DB level (small negative impact on performance but that seems a better approach no @Weiko ?)
  • finish update/migration commands
  • not important / after merging this, create an issue to see if we can learn from that unicity constraint error and build a mini DTO-framework like email-format validation or things like that in that validate method (?)

@charlesBochet charlesBochet mentioned this pull request Oct 7, 2024
4 tasks
variant: SnackBarVariant.Error,
},
);
enqueueSnackBar(`${error.message}`, {
Copy link
Member Author

Choose a reason for hiding this comment

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

was too dev-oriented

variant: SnackBarVariant.Error,
},
);
enqueueSnackBar(`Error finding duplicates:", ${error.message}`, {
Copy link
Member Author

Choose a reason for hiding this comment

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

was too dev-oriented

@@ -20,7 +20,7 @@ export const PromiseRejectionEffect = () => {
},
);
} else {
enqueueSnackBar(`Error: ${event.reason}`, {
enqueueSnackBar(`${error.message}`, {
Copy link
Member Author

Choose a reason for hiding this comment

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

red color and message usually already convey the fact that it's an error, and there's limited space

@@ -0,0 +1,53 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class MigrationDebt1726757368824 implements MigrationInterface {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is not directly related to this PR but it reflects that migrations <> typeorm entities were out of sync. I ran this a few weeks ago when opening the PR but it seems the debt hasn't been picked up by anyone since

Copy link
Member

Choose a reason for hiding this comment

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

Thanks! Ideally we should enforce a rule (danger?)
If *.entity.ts files are updated, a migration should be generated 🤔

import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';

@Command({
name: 'upgrade-0.31:enforce-unique-constraints',
Copy link
Member Author

Choose a reason for hiding this comment

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

This isn't ready to be reviewed yet

@@ -0,0 +1,53 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class MigrationDebt1726757368824 implements MigrationInterface {
Copy link
Member

Choose a reason for hiding this comment

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

Thanks! Ideally we should enforce a rule (danger?)
If *.entity.ts files are updated, a migration should be generated 🤔

import { WorkspaceActivationStatus } from '~/generated/graphql';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';

const filterTsVectorFields = (
Copy link
Member

Choose a reason for hiding this comment

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

I think we want to keep that?

Copy link
Member

Choose a reason for hiding this comment

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

Oh I see it seems you handled it differently 👍

@@ -40,7 +40,7 @@ export class CreateManyResolverFactory

return await this.graphqlQueryRunnerService.createMany(args, options);
} catch (error) {
workspaceQueryRunnerGraphqlApiExceptionHandler(error);
workspaceQueryRunnerGraphqlApiExceptionHandler(error, context);
Copy link
Member

Choose a reason for hiding this comment

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

should we use internalContext here as well to keep it consistent and avoid issues with _context?

@@ -114,6 +115,7 @@ export class TypeMapperService {
[FieldMetadataType.RAW_JSON, RawJsonFilterType],
[FieldMetadataType.RICH_TEXT, StringFilterType],
[FieldMetadataType.ARRAY, ArrayFilterType],
[FieldMetadataType.TS_VECTOR, StringFilterType],
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure we want that already. TS_VECTOR is dedicated to searchVector field and search is only applied in the search resolver (which does not contain filter). We might want to combine filters later on but I'm not sure this change is needed? This won't be properly reflected in the API at least, your filter won't work as expected with a StringFilter on a tsvector column

@FelixMalfait FelixMalfait marked this pull request as ready for review October 11, 2024 13:34
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

PR Summary

This pull request adds support for unique indexes and indexes on composite fields, focusing on enhancing data integrity and query performance. Key changes include:

  • Added indexMetadatas field to ObjectMetadataItem type, supporting index information retrieval
  • Introduced new types IndexMetadataItem and IndexFieldMetadataItem for handling index-related data
  • Updated GraphQL queries and schemas to include index metadata and uniqueness constraints
  • Modified database migrations to add isUnique and indexWhereClause fields to index metadata
  • Enhanced error handling in GraphQL resolvers for unique constraint violations
  • Removed filtering of TsVector fields in ObjectMetadataItemsLoadEffect, potentially exposing them in the UI

30 file(s) reviewed, 10 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines 1 to +2
import { useQuery } from '@apollo/client';
import { useMemo } from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Import order changed. Consider using a consistent import order throughout the codebase.

indexType: z.nativeEnum(IndexType),
indexWhereClause: z.string().nullable(),
isUnique: z.boolean(),
objectMetadata: z.any(),
Copy link
Contributor

Choose a reason for hiding this comment

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

style: objectMetadata: z.any() is too permissive. Define a more specific schema

Comment on lines +56 to +58
enqueueSnackBar(`Error finding duplicates:", ${error.message}`, {
variant: SnackBarVariant.Error,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Error message simplified, but consider including the object name for context

Comment on lines +22 to +24
enqueueSnackBar(`${error.message}`, {
variant: SnackBarVariant.Error,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider adding a generic prefix to error messages for consistency

Comment on lines +134 to +136
if (isFieldTsVector(fieldDefinition)) {
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: TsVector fields are always considered non-empty. Ensure this behavior is intended and doesn't cause unexpected side effects in other parts of the application.

Comment on lines 138 to 140
throw new Error(
`Entity field type not supported in isFieldValueEmpty : ${fieldDefinition.type}}`,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Consider using a more specific error type or including the field type in the error message for easier debugging.

Copy link
Member

@charlesBochet charlesBochet left a comment

Choose a reason for hiding this comment

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

Big one! Let's iterate from there!

@charlesBochet charlesBochet merged commit b792d2a into main Oct 13, 2024
8 of 9 checks passed
@charlesBochet charlesBochet deleted the add-unique-indexes branch October 13, 2024 08:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants