Skip to content

Commit

Permalink
Add relation functionality (#7653)
Browse files Browse the repository at this point in the history
  • Loading branch information
BykhovDenis authored Jan 14, 2025
1 parent c5c4af9 commit 582766b
Show file tree
Hide file tree
Showing 39 changed files with 1,177 additions and 62 deletions.
39 changes: 32 additions & 7 deletions models/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,43 @@
//

import {
DOMAIN_BLOB,
DOMAIN_CONFIGURATION,
DOMAIN_DOC_INDEX_STATE,
DOMAIN_MIGRATION,
DOMAIN_MODEL,
IndexKind,
type Account,
type AnyAttribute,
type ArrOf,
type Association,
type AttachedDoc,
type Blob,
type Card,
type Class,
type ClassifierKind,
type MarkupBlobRef,
type Collection,
type Configuration,
type ConfigurationElement,
type Doc,
type DocIndexState,
type Domain,
DOMAIN_BLOB,
DOMAIN_CONFIGURATION,
DOMAIN_DOC_INDEX_STATE,
DOMAIN_MIGRATION,
DOMAIN_MODEL,
DOMAIN_RELATION,
type DomainIndexConfiguration,
type Enum,
type EnumOf,
type FieldIndexConfig,
type FullTextSearchContext,
type IndexingConfiguration,
IndexKind,
type Interface,
type MarkupBlobRef,
type MigrationState,
type Mixin,
type Obj,
type PluginConfiguration,
type Ref,
type RefTo,
type Relation,
type Space,
type Timestamp,
type TransientConfiguration,
Expand Down Expand Up @@ -147,6 +150,28 @@ export class TAttachedDoc extends TDoc implements AttachedDoc {
collection!: string
}

@Model(core.class.Association, core.class.Doc, DOMAIN_MODEL)
export class TAssociation extends TDoc implements Association {
classA!: Ref<Class<Doc>>

classB!: Ref<Class<Doc>>

nameA!: string

nameB!: string

type!: '1:1' | '1:N' | 'N:N'
}

@Model(core.class.Relation, core.class.Doc, DOMAIN_RELATION)
export class TRelation extends TDoc implements Relation {
docA!: Ref<Doc<Space>>

docB!: Ref<Doc<Space>>

association!: Ref<Association>
}

@Model(core.class.Blob, core.class.Doc, DOMAIN_BLOB)
@UX(core.string.Object)
export class TBlob extends TDoc implements Blob {
Expand Down
4 changes: 4 additions & 0 deletions models/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { TBenchmarkDoc } from './benchmark'
import core from './component'
import {
TArrOf,
TAssociation,
TAttachedDoc,
TAttribute,
TBlob,
Expand All @@ -52,6 +53,7 @@ import {
TObj,
TPluginConfiguration,
TRefTo,
TRelation,
TTransientConfiguration,
TType,
TTypeAny,
Expand Down Expand Up @@ -155,6 +157,8 @@ export function createModel (builder: Builder): void {
TStatusCategory,
TMigrationState,
TBlob,
TRelation,
TAssociation,
TDomainIndexConfiguration,
TBenchmarkDoc,
TTransientConfiguration
Expand Down
14 changes: 14 additions & 0 deletions models/setting/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,20 @@ export function createModel (builder: Builder): void {
},
setting.ids.ClassSetting
)
builder.createDoc(
setting.class.WorkspaceSettingCategory,
core.space.Model,
{
name: 'relation',
label: core.string.Relations,
icon: setting.icon.Relations,
component: setting.component.RelationSetting,
group: 'settings-editor',
role: AccountRole.Maintainer,
order: 4501
},
setting.ids.Relations
)
builder.createDoc(
setting.class.WorkspaceSettingCategory,
core.space.Model,
Expand Down
1 change: 1 addition & 0 deletions models/setting/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default mergeIds(settingId, setting, {
NumberTypeEditor: '' as AnyComponent,
DateTypeEditor: '' as AnyComponent,
RefEditor: '' as AnyComponent,
AssociationEditor: '' as AnyComponent,
EnumTypeEditor: '' as AnyComponent,
General: '' as AnyComponent,
Owners: '' as AnyComponent,
Expand Down
5 changes: 4 additions & 1 deletion packages/core/lang/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
"AutoJoin": "Automatické připojení",
"AutoJoinDescr": "Automaticky připojit nové zaměstnance k tomuto prostoru",
"BlobSize": "Velikost",
"BlobContentType": "Typ obsahu"
"BlobContentType": "Typ obsahu",
"Relation": "Vztah",
"Relations": "Vztahy",
"AddRelation": "Přidat vztah"
}
}
5 changes: 4 additions & 1 deletion packages/core/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
"AutoJoin": "Automatisch beitreten",
"AutoJoinDescr": "Neue Mitarbeiter automatisch diesem Arbeitsbereich hinzufügen",
"BlobSize": "Größe",
"BlobContentType": "Inhaltstyp"
"BlobContentType": "Inhaltstyp",
"Relation": "Beziehung",
"Relations": "Beziehungen",
"AddRelation": "Beziehung hinzufügen"
}
}
5 changes: 4 additions & 1 deletion packages/core/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
"AutoJoin": "Auto join",
"AutoJoinDescr": "Automatically join new employees to this space",
"BlobSize": "Size",
"BlobContentType": "Content type"
"BlobContentType": "Content type",
"Relation": "Relation",
"Relations": "Relations",
"AddRelation": "Add relation"
}
}
5 changes: 4 additions & 1 deletion packages/core/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
"AutoJoin": "Auto unirse",
"AutoJoinDescr": "Unirse automáticamente a los nuevos empleados a este espacio",
"BlobSize": "Tamaño",
"BlobContentType": "Tipo de contenido"
"BlobContentType": "Tipo de contenido",
"Relation": "Relación",
"Relations": "Relaciones",
"AddRelation": "Añadir relación"
}
}
5 changes: 4 additions & 1 deletion packages/core/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
"AutoJoin": "Rejoindre automatiquement",
"AutoJoinDescr": "Ajouter automatiquement les nouveaux employés à cet espace",
"BlobSize": "Taille",
"BlobContentType": "Type de contenu"
"BlobContentType": "Type de contenu",
"Relation": "Relation",
"Relations": "Relations",
"AddRelation": "Ajouter une relation"
}
}
5 changes: 4 additions & 1 deletion packages/core/lang/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
"AutoJoin": "Partecipazione automatica",
"AutoJoinDescr": "Aggiungi automaticamente i nuovi dipendenti a questo spazio",
"BlobSize": "Dimensione",
"BlobContentType": "Tipo di contenuto"
"BlobContentType": "Tipo di contenuto",
"Relation": "Relazione",
"Relations": "Relazioni",
"AddRelation": "Aggiungi relazione"
}
}
5 changes: 4 additions & 1 deletion packages/core/lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
"AutoJoin": "Auto adesão",
"AutoJoinDescr": "Adesão automática de novos funcionários a este espaço",
"BlobSize": "Tamanho",
"BlobContentType": "Tipo de conteúdo"
"BlobContentType": "Tipo de conteúdo",
"Relation": "Relação",
"Relations": "Relações",
"AddRelation": "Adicionar relação"
}
}
5 changes: 4 additions & 1 deletion packages/core/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
"AutoJoin": "Автоприсоединение",
"AutoJoinDescr": "Автоматически присоединять новых сотрудников к этому пространству",
"BlobSize": "Размер",
"BlobContentType": "Тип контента"
"BlobContentType": "Тип контента",
"Relation": "Связь",
"Relations": "Связи",
"AddRelation": "Добавить связь"
}
}
5 changes: 4 additions & 1 deletion packages/core/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
"AutoJoin": "自动加入",
"AutoJoinDescr": "自动将新员工加入此空间",
"BlobSize": "大小",
"BlobContentType": "內容類型"
"BlobContentType": "內容類型",
"Relation": "关系",
"Relations": "关系",
"AddRelation": "添加关系"
}
}
25 changes: 25 additions & 0 deletions packages/core/src/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,26 @@ export interface UXObject extends Obj {
readonly?: boolean
}

/**
* @public
*/
export interface Association extends Doc {
classA: Ref<Class<Doc>>
classB: Ref<Class<Doc>>
nameA: string
nameB: string
type: '1:1' | '1:N' | 'N:N'
}

/**
* @public
*/
export interface Relation extends Doc {
docA: Ref<Doc>
docB: Ref<Doc>
association: Ref<Association>
}

/**
* @public
*/
Expand Down Expand Up @@ -357,6 +377,11 @@ export const DOMAIN_MIGRATION = '_migrations' as Domain
*/
export const DOMAIN_TRANSIENT = 'transient' as Domain

/**
* @public
*/
export const DOMAIN_RELATION = 'relation' as Domain

/**
* @public
*/
Expand Down
11 changes: 10 additions & 1 deletion packages/core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
Account,
AnyAttribute,
ArrOf,
Association,
AttachedDoc,
Blob,
Card,
Expand All @@ -43,6 +44,7 @@ import type {
Ref,
RefTo,
RelatedDocument,
Relation,
Role,
Space,
SpaceType,
Expand Down Expand Up @@ -108,6 +110,7 @@ export default plugin(coreId, {
Permission: '' as Ref<Class<Permission>>,
Account: '' as Ref<Class<Account>>,
Type: '' as Ref<Class<Type<any>>>,
TypeRelation: '' as Ref<Class<Type<string>>>,
TypeString: '' as Ref<Class<Type<string>>>,
TypeBlob: '' as Ref<Class<Type<Ref<Blob>>>>,
TypeIntlString: '' as Ref<Class<Type<IntlString>>>,
Expand Down Expand Up @@ -141,7 +144,9 @@ export default plugin(coreId, {
MigrationState: '' as Ref<Class<MigrationState>>,

BenchmarkDoc: '' as Ref<Class<BenchmarkDoc>>,
FullTextSearchContext: '' as Ref<Mixin<FullTextSearchContext>>
FullTextSearchContext: '' as Ref<Mixin<FullTextSearchContext>>,
Association: '' as Ref<Class<Association>>,
Relation: '' as Ref<Class<Relation>>
},
mixin: {
ConfigurationElement: '' as Ref<Mixin<ConfigurationElement>>,
Expand Down Expand Up @@ -184,6 +189,10 @@ export default plugin(coreId, {
String: '' as IntlString,
Record: '' as IntlString,
Markup: '' as IntlString,
Relation: '' as IntlString,
Relations: '' as IntlString,
AddRelation: '' as IntlString,
Collaborative: '' as IntlString,
CollaborativeDoc: '' as IntlString,
MarkupBlobRef: '' as IntlString,
Number: '' as IntlString,
Expand Down
44 changes: 43 additions & 1 deletion packages/core/src/memdb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@ import type { Account, Class, Doc, Ref } from './classes'
import core from './component'
import { Hierarchy } from './hierarchy'
import { checkMixinKey, matchQuery, resultSort } from './query'
import type { DocumentQuery, FindOptions, FindResult, LookupData, Storage, TxResult, WithLookup } from './storage'
import type {
AssociationQuery,
DocumentQuery,
FindOptions,
FindResult,
LookupData,
Storage,
TxResult,
WithLookup
} from './storage'
import type { Tx, TxCreateDoc, TxMixin, TxRemoveDoc, TxUpdateDoc } from './tx'
import { TxProcessor } from './tx'
import { toFindResult } from './utils'
Expand Down Expand Up @@ -155,6 +164,35 @@ export abstract class MemDb extends TxProcessor implements Storage {
return withLookup
}

private async fillAssociations<T extends Doc>(docs: T[], associations: AssociationQuery[]): Promise<WithLookup<T>[]> {
const withLookup: WithLookup<T>[] = []
for (const doc of docs) {
const result = await this.getAssoctionValue(doc, associations)
withLookup.push(Object.assign({}, doc, { $associations: result }))
}
return withLookup
}

private async getAssoctionValue<T extends Doc>(
doc: T,
associations: AssociationQuery[]
): Promise<Record<string, Doc[]>> {
const result: Record<string, Doc[]> = {}
for (const association of associations) {
const _id = association[0]
const assoc = this.findObject(_id)
if (assoc === undefined) continue
const isReverse = association[1] === -1
const key = !isReverse ? 'docA' : 'docB'
const key2 = !isReverse ? 'docB' : 'docA'
const _class = !isReverse ? assoc.classB : assoc.classA
const relations = await this.findAll(core.class.Relation, { association: _id, [key]: doc._id })
const objects = await this.findAll(_class, { _id: { $in: relations.map((r) => r[key2]) } })
result[_id] = objects
}
return result
}

async findAll<T extends Doc>(
_class: Ref<Class<T>>,
query: DocumentQuery<T>,
Expand Down Expand Up @@ -183,6 +221,10 @@ export abstract class MemDb extends TxProcessor implements Storage {
result = matchQuery(result, query, _class, this.hierarchy)
}

if (options?.associations !== undefined) {
result = await this.fillAssociations(result, options.associations)
}

if (options?.sort !== undefined) resultSort(result, options?.sort, _class, this.hierarchy, this)
const total = result.length
result = result.slice(0, options?.limit)
Expand Down
Loading

0 comments on commit 582766b

Please sign in to comment.