From 9eba1cdf07ee654e628c48739b933a6eb3244854 Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Wed, 26 Feb 2025 19:59:16 +0500 Subject: [PATCH] Fix relations (#8102) Signed-off-by: Denis Bykhov --- packages/query/src/index.ts | 2 +- .../src/components/RelationEditor.svelte | 24 +++++++++++++++---- .../src/components/RelationsEditor.svelte | 4 ++-- server/postgres/src/storage.ts | 11 ++++++--- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts index 9d3d4424af..d94fa7dde0 100644 --- a/packages/query/src/index.ts +++ b/packages/query/src/index.ts @@ -1103,7 +1103,7 @@ export class LiveQuery implements WithTx, Client { if (res === undefined) return const association = this.getModel().findObject(assoc[0]) if (association === undefined) return - const docToPush = await this.findOne(!direct ? association.classB : association.classA, { + const docToPush = await this.findOne(direct ? association.classB : association.classA, { _id: direct ? relation.docB : relation.docA }) if (docToPush === undefined) return diff --git a/plugins/view-resources/src/components/RelationEditor.svelte b/plugins/view-resources/src/components/RelationEditor.svelte index 83e5d0651f..c22fa688c5 100644 --- a/plugins/view-resources/src/components/RelationEditor.svelte +++ b/plugins/view-resources/src/components/RelationEditor.svelte @@ -31,8 +31,8 @@ if (result != null) { const client = getClient() await client.createDoc(core.class.Relation, core.space.Workspace, { - docA: direction === 'A' ? object._id : result._id, - docB: direction === 'A' ? result._id : object._id, + docA: direction === 'B' ? object._id : result._id, + docB: direction === 'B' ? result._id : object._id, association: association._id }) } @@ -76,13 +76,19 @@ ) } else { preferenceQuery.unsubscribe() + preference = undefined } - $: config = preference?.config ?? viewlet?.config + $: selectedConfig = preference?.config ?? viewlet?.config + $: config = selectedConfig?.filter((p) => + typeof p === 'string' + ? !p.includes('$lookup') && !p.startsWith('@') + : !p.key.includes('$lookup') && !p.key.startsWith('@') + ) async function onContextMenu (ev: MouseEvent, doc: Doc): Promise { const q = - direction === 'A' + direction === 'B' ? { docA: object._id, docB: doc._id, association: association._id } : { docA: doc._id, docB: object._id, association: association._id } const relation = await client.findOne(core.class.Relation, q) @@ -90,12 +96,20 @@ showMenu(ev, { object: relation, includedActions: [view.action.Delete] }) } } + + function isAllowedToCreate (association: Association, docs: Doc[], direction: 'A' | 'B'): boolean { + if (docs.length === 0 || association.type === 'N:N') return true + if (association.type === '1:1') return false + return direction === 'B' + } + + $: allowToCreate = isAllowedToCreate(association, docs, direction)
- {#if !readonly} + {#if !readonly && allowToCreate}
diff --git a/plugins/view-resources/src/components/RelationsEditor.svelte b/plugins/view-resources/src/components/RelationsEditor.svelte index a4fe577bdf..7be498a3aa 100644 --- a/plugins/view-resources/src/components/RelationsEditor.svelte +++ b/plugins/view-resources/src/components/RelationsEditor.svelte @@ -52,7 +52,7 @@ (res) => { relationsA = res?.[0]?.$associations ?? {} }, - { associations: associationsA.map((a) => [a._id, 1]) } + { associations: associationsA.map((a) => [a._id, -1]) } ) const queryB = createQuery() @@ -62,7 +62,7 @@ (res) => { relationsB = res?.[0]?.$associations ?? {} }, - { associations: associationsB.map((a) => [a._id, -1]) } + { associations: associationsB.map((a) => [a._id, 1]) } ) diff --git a/server/postgres/src/storage.ts b/server/postgres/src/storage.ts index b3ff7f1343..cc0cf4466f 100644 --- a/server/postgres/src/storage.ts +++ b/server/postgres/src/storage.ts @@ -877,9 +877,13 @@ abstract class PostgresAdapterBase implements DbAdapter { } } } else if (column.startsWith('assoc_')) { + if (row[column] == null) continue const keys = column.split('_') const key = keys[keys.length - 1] - associations[key] = row[column] + const associationDomain = keys[1] + const associationSchema = getSchema(associationDomain) + const parsed = row[column].map((p: any) => parseDoc(p, associationSchema)) + associations[key] = parsed } else { joinIndex = undefined if (!map.has(row._id)) { @@ -1434,17 +1438,18 @@ abstract class PostgresAdapterBase implements DbAdapter { } const isReverse = association[1] === -1 const _class = isReverse ? assoc.classA : assoc.classB + const tagetDomain = translateDomain(this.hierarchy.getDomain(_class)) const keyA = isReverse ? 'docB' : 'docA' const keyB = isReverse ? 'docA' : 'docB' const wsId = vars.add(this.workspaceId, '::uuid') res.push( `(SELECT jsonb_agg(assoc.*) - FROM ${translateDomain(this.hierarchy.getDomain(_class))} AS assoc + FROM ${tagetDomain} AS assoc JOIN ${translateDomain(DOMAIN_RELATION)} as relation ON relation."${keyB}" = assoc."_id" AND relation."workspaceId" = ${wsId} WHERE relation."${keyA}" = ${translateDomain(baseDomain)}."_id" - AND assoc."workspaceId" = ${wsId}) AS assoc_${association[0]}` + AND assoc."workspaceId" = ${wsId}) AS assoc_${tagetDomain}_${association[0]}` ) } return res