Aller au contenu

Enregistrement des résultats pivots ​

Pivots & enrichissement se termine par une ingération : les candidats choisis sont sur le canevas, tagués du pivot qui les a amenés, et nulle part ailleurs. save est le pas en arrière : le pivot écrit ses propres résultats dans le système d'où ils proviennent, et la bibliothèque conserve les livres sur ce qui a traversé.

summarize  →  fetch  →  triage  →  ingest  →  save
  "2,143"     the 210    the 12    on canvas   in the source system

La bibliothèque n'écrit jamais elle-même nulle part et ne détient aucune information d'identification. Ce qu'il apporte, c'est la comptabilité - quels éléments une exécution a créés, s'ils ont été écrits, lesquels ont échoué et ce qu'une nouvelle tentative doit contenir - car c'est la partie qu'il est le seul à connaître.

En déclarer un ​

js
const objects = {
    id: 'objects',
    label: 'Objects & attributes',
    fetch: (nodes, narrowing, ctx) => api.objects(nodes[0].id, { signal: ctx.signal }),

    save: async ({ origin, nodes, children, edges }, ctx) => {
        const written = await api.createObjects(origin[0].id, [...nodes, ...children], {
            signal: ctx.signal,
        })
        return {
            savedNodeIds: written.ok.map(o => o.localId),
            savedEdgeIds: edges.map(e => e.id),
            canonicalIds: Object.fromEntries(written.ok.map(o => [o.localId, o.uuid])),
            message: written.failed.length ? `${written.failed.length} refused` : undefined,
        }
    },
}

La charge utile est constituée d'objets graphiques dynamiques, et non de fragments bruts :

ChampCe qu'il contient
originsur quoi le pivot a été exécuté - "à quel événement cela s'attache-t-il"
nodesles nœuds créés par cette exécution
childrencontenu du conteneur ajouté par l'union, aplati ; chacun accessible depuis son conteneur via parentNode
edgesles bords créés par cette course
vouchedce qui était déjà sur la toile avant cette course
attempt1 la première fois, plus élevé lors d'une nouvelle tentative

Omettez save et les résultats du pivot sont ne peuvent pas être sauvegardés. Ils n'entrent jamais dans le grand livre, ne sont jamais comptés non enregistrés et aucune sauvegarde n'apparaît pour eux. C'est la bonne déclaration pour un pivot sur les données dérivées - la sortie d'un moteur de corrélation ne vous appartient pas de réécrire - et c'est ce qui arrête un "210 non sauvegardé" permanent sans aucun remède.

Ce que tu retournes ​

RetournéMoyens
undefined / truetoute la charge utile a été écrite
false, ou lancerrien de tout cela ne l’était ; l'erreur atteint le toast de nouvelle tentative
{ savedNodeIds, savedEdgeIds }une écriture partielle — tout ce qui n'est pas nommé ne reste pas enregistré
{ canonicalIds }identifie le système source attribué, saisi par l'identifiant local
{ message }montré textuellement dans le toast des résultats
{ cancelled: true }l'analyste s'est retiré : rien d'enregistré, pas de toast, pas de tentative

savedNodeIds couvre nodes et children ensemble. Un identifiant nommant un élément que cette exécution n'a pas créé est ignoré : un pivot n'écrit jamais ce qu'il n'a pas produit, il ne peut donc pas non plus le signaler écrit.

Une sauvegarde qui signale une arête écrite mais pas les nœuds qu'elle connecte est prise au pied de la lettre. Ce que le système source dit avoir écrit ne doit pas être annulé par la bibliothèque.

Demander d'abord à l'analyste ​

Une écriture a parfois besoin d'une réponse avant de pouvoir être exécutée, par exemple quelle relation lie chaque résultat au nœud à partir duquel il a été enrichi. ctx.promptData ouvre le propre modal de la bibliothèque, avec les mêmes options que celui derrière la création de nœuds et de bords : déclaratif fields, ou render + getValues pour votre propre HTML. Il se résout aux valeurs, ou null en cas d'annulation.

js
save: async (payload, ctx) => {
    const answer = await ctx.promptData({
        fields: [{ key: 'relationship', label: 'Relationship', type: 'text', defaultValue: 'related-to' }],
    })
    if (!answer) return { cancelled: true }
    await api.attach(payload, answer.relationship, { signal: ctx.signal })
},

Le modal est intitulé et soumis avec Save sauf si title et submitLabel disent le contraire. Le retour de { cancelled: true } laisse l'exécution exactement telle qu'elle était avant le clic : toujours non enregistrée, pas de toast, pas de nouvelle tentative, et la tentative suivante est toujours attempt: 1. Une sauvegarde sur plusieurs exécutions s'arrête à la première annulation et le rapport la compte sous cancelled plutôt que comme un échec. Une nouvelle tentative annulée remet son toast tel qu'il était.

Seule une sauvegarde sur laquelle quelqu'un a cliqué peut demander. L'enregistrement du panneau Pivot, l'enregistrement du volet Révision et la tentative de réessai du toast interactive: true ; autoSave et un simple graph.pivots.save() ne le font pas, donc leur promptData résout null à la fois sans rien dessiné. Un hôte doté de son propre bouton Enregistrer transmet également le drapeau :

js
await graph.pivots.save(undefined, { interactive: true })

En demander un ​

js
await graph.pivots.save()          // every savable run with something still unsaved
await graph.pivots.save(runId)     // one run
await graph.pivots.save(pivotId)   // every unsaved run of one pivot

graph.pivots.unsaved()             // the runs still waiting
graph.pivots.unsavedCount()        // { nodes, edges } — exact, not advisory
graph.pivots.unsavedCount(pivotId) // one pivot's share of it
graph.pivots.isSaved(node)         // false for the unsaved *and* the not-savable
graph.pivots.isSavable(node)       // which of those two it is

Une partie d'une course ​

js
const selected = graph.renderer.getGraphInteraction().getSelectedNodeIDs() ?? []
await graph.pivots.save({ elements: selected })   // Nodes, Edges or their ids

Chaque exécution qui a créé l'un des éléments nommés est interrogée une fois, avec une charge utile limitée à ce qui a été nommé. Un nœud nommé amène les enfants en attente sous lui, et un bord apparaît lorsqu'une extrémité est nommée et que l'autre est également nommée ou ne sera pas laissée non écrite : l'origine, un nœud déjà enregistré, tout ce que le grand livre n'attend pas. Un bord vers un nœud en attente laissé de côté reste avec lui, donc une écriture n'envoie jamais de ligne à rien. Les éléments nommés que rien n'attend sont ignorés et l'appel reçoit un toast pour chaque exécution demandée.

Le reste reste en attente et compte, Réessayez et canonicalIds fonctionnent comme ils le font pendant toute une exécution. Une exécution est marquée comme persistante dans l'historique seulement une fois qu'il ne reste plus rien à écrire. isSavable(el) et isSaved(el) indiquent si une entrée de menu comme Enregistrer la sélection a quelque chose à faire.

Les exécutions se déroulent une par une, et chacune est envoyée uniquement ce qui n'est pas encore enregistré — donc une nouvelle tentative est le même appel. Le résultat arrive sous la forme d'un toast : Saved 12 nodes, ou Saved 9 of 12 avec un Retry qui reprend ce même toast plutôt que d'en empiler un deuxième.

Dans l'interface utilisateur, le décompte et le bouton se trouvent au pied du panneau Pivot, et un volet Révision comporte la part de son propre fournisseur dans son en-tête. Rien n'apparaît tant que le nombre est nul.

Les deux écrivent tout en attente, sans aucun moyen de choisir. Une application qui propose une sauvegarde ailleurs, comme une entrée Enregistrer la sélection dans le menu du nœud, peut les désactiver :

js
new Pivotick(el, data, { pivots: [objects], pivotSaveControls: false })
graph.pivots.saveControls = true   // and back, at runtime

Seuls les deux contrôles fonctionnent. Le grand livre, les comptes, save(...), son toast et sa nouvelle tentative, et canonicalIds fonctionnent comme avant.

autoSave: true écrit chaque exécution au moment où elle atterrit, sans geste. Il s'exécute après la résolution de l'ingestion plutôt qu'à l'intérieur de celui-ci, de sorte qu'un backend lent ne retarde jamais le canevas, et un échec est signalé via le notificateur et laisse les données là où elles se trouvent. Associez-le à autoIngest: true pour un pivot sans intervention de bout en bout – étendant un événement en objets qui appartiennent déjà à la source.

Identifie les menthes du système source ​

L'enregistrement crée généralement quelque chose, et l'élément créé obtient généralement un identifiant choisi par le système source, et non celui utilisé par votre fournisseur lorsqu'il était candidat. L'exécution de demain renvoie ensuite les mêmes objets sous des identifiants que la déduplication ne reconnaît pas, et l'analyste obtient douze doublons de ce qu'il a enregistré hier.

Renvoyez canonicalIds et la bibliothèque conserve l'alias. Ingérez la déduplication, l'union des enfants et les points de terminaison Edge le consultent tous, donc la réexécution indique 12 déjà sur le canevas à la place.

js
graph.pivots.canonicalId(node)   // 'a1b2…' — the id the source assigned
Pourquoi le nœud conserve l'identifiant sous lequel il a atterri

La re-saisie porterait sur les bords, les clusters, la sélection, le moteur de requête, la provenance et l'historique, pour un avantage que l'alias offre déjà là où cela compte. Le coût honnête : graph.getNode(canonicalId) manque toujours, et canonicalId() est la lecture qui ne manque pas.

L'annulation n'atteint pas le système source ​

L'annulation d'une exécution enregistrée supprime ses nœuds du canevas. Il ne supprime pas quoi que ce soit du système sur lequel il a été écrit, et la bibliothèque n'émet aucune écriture compensatoire : l'annulation d'une vue locale n'est pas une transaction entre deux systèmes.

L'historique le dit plutôt que de vous laisser le découvrir : une course marquée comme écrite est ébréchée saved, et le pied de page du menu compte ce qu'une durée laisserait derrière elle. PivotRun porte un décompte saved pour une surface qui souhaite avertir avant le clic. Voir Annuler et historique pour savoir comment il traite une entrée persistante.

Une sauvegarde ratée ne touche jamais non plus le canevas. L'analyste a accepté ces douze nœuds ; un refus du backend est une information sur le backend, pas un renversement de sa décision. Ils restent, ne sont pas enregistrés et peuvent être réessayés.

Marquage des nœuds non enregistrés ​

js
new Pivotick(el, data, { pivots: [objects], pivotMarkUnsaved: true })

Place une classe pvt-node-unsaved sur chaque nœud créé par une exécution et n'a pas été réécrit, qu'une feuille de style peut récupérer - la valeur par défaut est un bord en pointillés. Désactivé par défaut, et une classe plutôt qu'un badge de jante : un nœud a quatre coins de jante et seulement deux une fois qu'il a des enfants, et un marqueur qui repousse un potentiel déclaré hors de la jante coûte plus cher qu'il ne le dit.