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

feat(minato): relation #90

Merged
merged 62 commits into from
May 19, 2024
Merged

feat(minato): relation #90

merged 62 commits into from
May 19, 2024

Conversation

Hieuzest
Copy link
Contributor

@Hieuzest Hieuzest commented Apr 22, 2024

Usage

Declaration

  • Type: Relation
    • type: oneToOne, oneToMany, manyToOne, manyToMany
    • table: relation table
    • target: relation field
    • fields: MaybeArray: key fields in current table
    • references: MaybeArray: key fields in relation table
interface User {
	id: number
	value?: number
	profile?: Relation<Profile>
	posts?: Relation<Post[]>
	successor?: Relation<{ id: number }>
}

interface Profile {
	id: number
	name?: string
	user?: Relation<User>
}

interface Post {
	id: number
	score?: number
	author?: Relation<User>
	content?: string
	tags?: Relation<Tag[]>
}

interface Tag {
	id: number
	name: string
	posts?: Relation<Post[]>
}

  database.extend('profile', {
    id: 'unsigned',
    name: 'string',
  })

  database.extend('user', {
    id: 'unsigned',
    value: 'integer',
    successor: {
      type: 'oneToOne',
      table: 'user',
    },
    profile: {
      type: 'oneToOne',
      table: 'profile',
      target: 'user',
    },
  }, {
    autoInc: true,
  })

  database.extend('post', {
    id: 'unsigned',
    score: 'unsigned',
    content: 'string',
    author: {
      type: 'manyToOne',
      table: 'user',
      target: 'posts',
    },
  }, {
    autoInc: true,
  })

  database.extend('tag', {
    id: 'unsigned',
    name: 'string',
    posts: {
      type: 'manyToMany',
      table: 'post',
      target: 'tags',
    },
  }, {
    autoInc: true,
  })

Select

Relation fields must be explicitly included in a selection when needed.

  • database.get('user', {}, ['posts'])
  • database.select('user, {}, { posts: true })
  • database.select('user, {}, { posts: { tags: true } })
    (nested reads)
  • database.get('user', { posts: ... })
    (if occurs in the top level of the query, automatically include it)

Query

  • toOne:
    Query.Expr | Selection.Callback | Query.Callback

shorthand not allowed

database.get('user', {
	profile: {
		userId: 1,
	},
})

database.get('user', {
	profile: row => $.eq(row.userId, 1),
})

database.get('user', {
	profile: null, // existense
})

database.get('user', row => $.query(row, {
	profile: r => $.eq(r.userId, row.id),
}))
  • toMany
$some?: T extends Relation<(infer I)[]> ? Query<I> : never
$none?: T extends Relation<(infer I)[]> ? Query<I> : never
$every?: T extends Relation<(infer I)[]> ? Query<I> : never

database.get('user', {
	posts: {
		$none: {
			authorId: 1,
		},
	},
})

database.get('user', {
	posts: {
		$some: row => $.eq(row.id, 1),
	},
})
  • nested
database.get('user', {
	posts: {
		$some: {
			tags: {
				$some: {
					id: 1,
				},
			},
		},
	},
})

Create

as normal
nested create is supported
manyToMany in create is not allowed

Update

  • toOne:

as normal

await database.set('user', 3, {
	profile: {
		name: 'Reborn',
	},
})
  • toMany
export type SetExpr<S extends object = any> = Row.Computed<S, Update<S>> | {
	  where: Query.Expr<Flatten<S>> | Selection.Callback<S, boolean>
	  update: Row.Computed<S, Update<S>>
}

export interface Modifier<S> {
	// oneToMany
    $create?: MaybeArray<Partial<Create<S>>>
    $set?: MaybeArray<SetExpr<S>>
    $remove?: Query.Expr<Flatten<S>> | Selection.Callback<S, boolean>
	// nullable oneToMany / manyToMany
    $connect?: Query.Expr<Flatten<S>> | Selection.Callback<S, boolean>
    $disconnect?: Query.Expr<Flatten<S>> | Selection.Callback<S, boolean>
}

database.set('user', 1, row => ({
	posts: {
		$create: { content: 'new post' },
		$set: r => ({ score: $.add(r.score, 10) }),
		$remove: { score: { $lt: 5} },
	},
}))
  • Execution order:

oneToMany: $remove -> $set -> $create
manyToMany: $disconnect -> $connect

Restrictions

  • remove() with relation query is not supported for mongodb
  • set() with possible duplicated oneToOne updating is not allowed

Changes on existing behaviours

  • bring back $expr in FieldQuery and add Query.Callback
export type Callback<T = any> = (row: Row<T>) => Expr<Flatten<T>>

so that one can write row => ({ $expr: ...., id: 1, posts: ... }) i.g. write callback, fieldquery, relquery togather

  • introduce $.in/nin(any[], any[][]) and Selection.evaluate(K[]) for composed keys querying
$.in([row.x, row.y], database.select('foo').evaluate(['x', 'y']))

Other notable features

  • Selection.join(name, sel, callback, optional)
    Difference between database.join:
    • return type is { ...A, [name]: B }
    • field [name] is automatically wrapped in $.ignoreNull

Tasks

  • select (nested)
  • create (nested)
  • query
  • set 1-M (create, set, remove)
  • set M-M (connect, disconnect)
  • upsert

Possible prerequistites:

  • update join / delete join
  • create index (maybe in another pr)
  • update expr

Copy link

codecov bot commented Apr 22, 2024

Codecov Report

Attention: Patch coverage is 99.83278% with 3 lines in your changes are missing coverage. Please review.

Project coverage is 98.79%. Comparing base (f178a94) to head (ed10880).

Files Patch % Lines
packages/core/src/database.ts 99.33% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #90      +/-   ##
==========================================
+ Coverage   98.65%   98.79%   +0.14%     
==========================================
  Files          38       39       +1     
  Lines       10031    11613    +1582     
  Branches     2595     3019     +424     
==========================================
+ Hits         9896    11473    +1577     
- Misses        135      140       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Hieuzest Hieuzest requested a review from shigma April 30, 2024 15:34
@shigma shigma merged commit aade306 into cordiverse:master May 19, 2024
26 checks passed
@Hieuzest Hieuzest deleted the feat-relation branch May 19, 2024 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants