Schwerpunkte und Bereicherung
Ein Pivot ist eine ausführbare Anreicherung: „Zeigen Sie mir, was die Quelle sonst noch dazu sagt“. Sie deklarieren es, die Bibliothek führt es aus, stellt die Ergebnisse zur Triage bereit und überträgt sie mit der Herkunft jedes Elements in das Diagramm, sobald der Analyst dies angibt.
Das dadurch gelöste Problem besteht darin, dass der interessante Knoten normalerweise zu viele Verbindungen hat. Ein einzelner Indikator kann mit Tausenden anderen in Beziehung gesetzt werden. Bei einem Pivot handelt es sich also um zwei Anrufe und nicht um einen: einen billigen Anruf, der sagt, was da draußen ist, und einen teuren Anruf, der immer nur über eine Nummer läuft, der der Analyst zugestimmt hat.
Vokabeln, die unten überall einheitlich verwendet werden
- pivot: die ausführbare Anreicherung
- Anbieter: die beiden von Ihnen bereitgestellten Funktionen
- Kandidaten: abgerufene Ergebnisse, noch nicht in der Grafik
- ingest: Übertragen der ausgewählten Kandidaten in die Grafik
- Ablehnen: eine ausdrückliche Entlassung, die für die Sitzung gespeichert wird
- Quelle: Das Herkunftskennzeichen, das auf alles geschrieben ist, was ein Pivot landet
- speichern: Ein aufgenommenes Ergebnis wird zurückgeschrieben out, in das System, aus dem es stammt
- ungespeichert: auf der Arbeitsfläche, noch nicht zurückgeschrieben – eine genaue Zählung, keine Schätzung
summarize → fetch → triage → ingest → save
"2,143" the 210 the 12 on canvas in the source systemEs ist nur fetch erforderlich. Alles andere in dieser Zeile ist ein Schritt, für den Sie sich entscheiden.
Sehen Sie, wie es funktioniert: Pivotieren und Anreichern für den verengten Fluss und Ablehnen und erneut versuchen für Triage- und Fehlerzustände.
Die Verwendung eines ist, was ein Analyst in der Reihenfolge tut und was jeder Schritt ein Backend kostet. Alles ab Einen registrieren aufwärts ist die Referenz für denjenigen, der einen verkabelt, und Was Ihre Quelle zu bieten hat ist derselbe Vertrag, der von der Seite des Dienstes gelesen wird.
Mit einem
1 · Wechseln Sie in den Pivot-Modus. Drücken Sie P oder klicken Sie auf den Schlitz Pivot auf der linken Schiene. Der Slot existiert nur, solange mindestens ein Pivot registriert ist. Von keinem Backend wurde noch etwas abgefragt, und bei der Auswahl von Knoten bei geschlossenem Modus wird auch nichts abgefragt. Wenn Sie 50 Knoten per Kästchen auswählen, um sie zu ziehen, sollten keine 50-Count-Abfragen ausgelöst werden.
2 · Wählen Sie einen Ursprung. Der Ursprung ist die Knotenmenge, um die sich die Frage dreht. Ursprung auswählen ist das ruhende Werkzeug, also wird es durch einen einfachen Klick festgelegt. Lasso-Ursprung zeichnet mehrere um. Was auch immer bereits ausgewählt war, als Sie den Modus aufgerufen haben, wird zum Ursprung, und Löschen leert es. Ein leerer Ursprung hat immer noch Verwendungsmöglichkeiten: Die mit origin: 'none' deklarierten Pivots gelten, wenn nichts ausgewählt wird.
3 · Lesen Sie, was zutrifft. Der Eintritt in den Modus ist die Absicht, die summarize einmal pro anwendbarem Pivot ausführt, gestapelt in einem einzigen Aufruf für einen Ursprung mit mehreren Knoten. Jeder Eintrag zeigt, was sein Anbieter behauptet, da draußen, geschrieben als ~2,143. Die Tilde markiert eine Zahl, die der Anbieter behauptet hat, und nicht eine, die die Bibliothek gezählt hat. Ein Pivot ohne summarize zeigt stattdessen ein bloßes Run an. Wenn nichts zutrifft, sagt das Panel dies und zeigt keine leere Liste an.
4 · Eng, bis sich das Tor öffnet. Ein Pivot, der maxCandidates deklariert, hält Fetch außerhalb der Reichweite, während die Anzahl diesen überschreitet, und gibt die Anzahl, das Limit und den weiteren Weg an. Durch Aktivieren eines einschränkenden Steuerelements wird summarize mit dieser Auswahl erneut abgefragt, sodass 2.143 zu 210 wird und Fetch von selbst aktiviert wird. Deutliche Verengung beginnt von vorne. Daneben platziert Fetch & ingest jedes Ergebnis ohne Überprüfung im Diagramm. Es wird nur angeboten, wenn der Pivot autoIngest nicht eingestellt ist.
5 · Sortieren Sie, was zurückgegeben wurde. Die Ergebnisse berühren nicht die Grafik. Sie öffnen die Registerkarte Review im unteren Dock, einen Bereich pro von Ihnen ausgeführtem Anbieter, der an der Seite aufgelistet ist. Klicken Sie irgendwo auf eine Zeile, um sie zu markieren, klicken Sie bei gedrückter Umschalttaste, um die Markierung über einen Bereich zu übertragen, und Alle n Übereinstimmungen auswählen berücksichtigt den aktuellen Filter; Zeilen durchsuchen… Filter (Regex optional) und die Spalten sortieren. Ausgewählte ablehnen und Alle verbleibenden ablehnen entsorgen Zeilen explizit und werden für die Sitzung gespeichert, während × lediglich einen Bereich schließt und nichts ablehnt. Erneut ausführen fragt noch einmal mit der gleichen Verengung. Am Fuß des Streifens löscht Schließen fehlgeschlagen jeden Abruf, der nichts zurückbrachte, und Alle schließen leert die Warteschlange; keiner lehnt etwas ab. Kandidaten sind nicht die Grafik Der Rest ist da.
6 · Aufnehmen und rückgängig machen, wenn es falsch war. Ausgewählte Aufnahme (12) schreibt genau diese zwölf fest, platziert sie um den Knoten, von dem aus Sie geschwenkt haben, und markiert den Pivot als Quelle. Ingest all 213 daneben überspringt die Markierung und übernimmt alles, was der Anbieter noch anbietet, unabhängig davon, nach welcher Tabelle gefiltert wird. Der Toast lautet Ingested 12 nodes, 14 edges. Die Rücknahme erfolgt über das Rückgängig-Steuerelement in der oberen Leiste, das dauerhaft vorhanden ist und nicht so lange, wie ein Toast dauert, und den gesamten Lauf rückgängig macht – einschließlich seiner Kanten und aller untergeordneten Elemente, mit denen er zusammengeführt wurde.
7 · Speichern Sie es zurück, wenn der Pivot dies kann. Ingest platziert die Zwölf auf der Arbeitsfläche; es schreibt sie nirgendwo hin. Am Fuß des Pivot-Bedienfelds wird angezeigt, wie viele Elemente diese Sitzung eingeholt und nicht zurückgeschrieben hat, und Speichern schreibt sie. Ein Pivot, der Nein save angibt, trägt nie zu dieser Zahl bei – siehe Pivot-Ergebnisse speichern.
Bei einer Aufnahme, die nichts zu entscheiden lässt, wird das Fenster geschlossen und der Streifen geht zum nächsten wartenden Anbieter über. Reste halten es offen, ebenso wie eine Wiederholung, die darin wartet. Sobald der letzte Bereich verschwindet, wird ein Dock, das die Rezension geöffnet hat, an die Stelle zurückgeklappt, an der es sich befand, sodass ein Pivot das gefundene Layout verlässt.
Der One-Click-Pivot
Die Schritte 2 bis 4 beziehen sich auf eine Frage, die es wert ist, zunächst gelesen zu werden. Die meisten sind es nicht. Klicken Sie mit der rechten Maustaste auf einen Knoten und Pivot ▸ listet jeden Pivot auf, der auf ihn zutrifft, mit dem, was jeder einzelne ankündigt. Wenn Sie eine Zeile auswählen, wird sie dort und dann ausgeführt: kein Bereich, nichts zum Ausfüllen. Der Ursprung ist die Auswahl, wenn der angeklickte Knoten dazu gehört und dieser Knoten ansonsten allein, sodass ein Massen-Pivot erreichbar ist, ohne das Menü zu verlassen.
Das Öffnen des Untermenüs ist ebenfalls die Frage. Jedem Pivot mit einem summarize wird die eigene Frage des Gremiums gestellt, dieser Ursprung ohne Eingrenzung, und seine Zeile enthält einen Platzhalter, bis die Zählung eintrifft: ~2,143, beratend wie jede Zahl, die ein Anbieter bewirbt. Ein Pivot, der nichts ankündigt, zeigt nichts an und läuft trotzdem. Die Fragen werden erst ausgeblendet, wenn das Untermenü einen Moment lang gehalten wurde. Wenn also der Zeiger auf dem Weg zu einer anderen Stelle Pivot ▸ kreuzt, werden keine Fragen gestellt und die Fragen werden gelöscht, wenn das Untermenü zuerst geschlossen wird. Die Antworten werden in beide Richtungen zwischengespeichert: Ein zweiter Blick zeigt sie sofort an, und das Panel verwendet erneut, was das Menü gelernt hat. Ein Register, das länger als das Menü ist, kann Schriftrollen enthalten, die in der Mitte der Zeile abgeschnitten werden, sodass es als fortlaufend angezeigt wird.
Wohin die Ergebnisse gelangen, hängt davon ab, wie viele davon neu auf der Arbeitsfläche sind:
- Bis zu
pivotQuickIngestLimitneue Kandidaten (standardmäßig 50) landen direkt, sind markiert und können wie jede andere Aufnahme rückgängig gemacht werden. - Mehr als das Öffnen Sie stattdessen die Registerkarte Review, genau wie bei einem Panellauf.
- Eine Cap-Ablehnung erfolgt, bevor etwas abgerufen wird, es gibt also nichts zu überprüfen. Das Pivot-Panel öffnet sich an diesem Pivot mit der Nummer und der Verengung, die es anhebt.
Eine Reihe läuft, ohne dass etwas eingeengt wird. Wenn ein Pivot tatsächlich aus mehreren Läufen besteht, zwischen denen der Analyst wählt, z. B. die Anreicherung eines Objekts durch eines seiner Attribute, deklarieren Sie menuChoices: Die Zeile öffnet dann ein Untermenü mit einer Zeile pro Auswahl, wobei jeder Pivot mit seiner eigenen Verengung ausgeführt wird, und eine letzte Zeile öffnet das Panel für diesen Pivot. Es ist synchron und wird jedes Mal ausgeführt, wenn das Menü geöffnet wird, z. B. appliesTo. Eine Obergrenze für die Ablehnung einer Auswahl öffnet das Panel zur Einengung der Auswahl. Geben Sie [] für eine einfache Zeile zurück.
menuChoices: ([object]) => attributesOf(object).map((a) => ({
label: `${a.type}: ${a.value}`,
narrowing: { attribute: [a.id] },
})),Kandidaten, die sich bereits auf der Arbeitsfläche befinden, sind nicht neu und füllen nie das Limit aus. Das erneute Ausführen eines Pivots über dem von Ihnen abgedeckten Boden bleibt also eine Ein-Klick-Geste. Eine Kante zwischen zwei bereits auf dem Bildschirm angezeigten Knoten zählt, da es sich um eine Zeile handelt, nach der Sie sonst gefragt worden wären. Ein Pivot, der autoIngest deklariert hat, hat die Frage bereits beantwortet und seine Antwort lautet: true landet immer, false überprüft immer. Das Limit entscheidet nur für Pivots, die es nicht gesetzt haben, und nur für diese Geste; Ein Panel Fetch wird immer inszeniert.
Bis die Antwort bekannt ist, hat der Lauf nichts geboten, daher ist kein Bildschirm erforderlich, um zu sagen, dass er funktioniert. Eine Registerkarte Review verbindet sich mit der Leiste Review… des Docks, während der Abruf ausgeführt wird, ohne dass das Dock aufgeklappt oder ein Fenster nach vorne bewegt werden muss; Klicken Sie darauf, um den Lauf anzusehen oder ihn abzubrechen. Nur ein Ergebnis, über das entschieden werden muss, wird angezeigt, und ein Run, der landet, nimmt seinen Vorsprung mit. Die beiden Antworten, bei denen es nichts zu entscheiden gibt, sind die des Melders: Ein Abruf fehlgeschlagen sagt dies mit einem Wiederholungsversuch aus, und ein Ergebnis über dem absoluten Wert candidateCeiling gibt die Anzahl und das Limit an.
Es gibt also drei Wege hinein: den Schienenmodus, das Kontextmenü Pivot ▸ eines Knotens (abwesend, nie deaktiviert, wenn nichts zutrifft) und, mit pivotRimBadgeVisible: 'always', ein Felgenabzeichen, das den Modus öffnet, der sich auf seinen eigenen Pivot bezieht, oder, wenn die Felge für alle ein Abzeichen trägt, auf den Knoten. Ein Abzeichen für einen Knoten mit genau einem Pivot, der nichts einschränkt, führt ihn direkt aus, wie eine Menüzeile.
Eines registrieren
Drei Türen, ein Register, in der Reihenfolge, in der Sie danach greifen würden:
// 1. Options, at construction. Lands before the UI is built, so the rail button is
// already right on the first paint.
const graph = new Pivotick(el, data, { pivots: [correlations] })
// 2. Imperatively, at any point. Returns a disposer.
const remove = graph.pivots.register(correlations)
// 3. From a plugin's install, beside addPanel / addDockTab / addRailMode.
ctx.addPivot(correlations)graph.pivots ist sowohl die Registrierung als auch die Laufzeit. for(), run(), undo(), invalidate() und das Kandidatenmodell hängen alle davon ab.
Ein Komplettanbieter
Alles, was ein funktionierender Pivot braucht, ohne dass nichts ausgelassen wird. Der Rest dieser Seite dient als Referenz für jedes Stück; Dies ist die Sache, die zuerst kopiert und dann gekürzt werden muss.
post unten ist Ihr eigener JSON-Helfer – er wirft einen Nicht-2xx an und leitet signal durch, sodass der Pivot als Vertrag und nicht als HTTP gelesen wird.
const correlations = {
id: 'correlations', // the provenance tag written on everything it lands
label: 'Correlations', // shown verbatim, so translate it yourself
maxCandidates: 2000, // refuse to fetch while the count is above this
// Which nodes this offers itself for — return the ones you accept, and a mixed
// selection runs it against just those. Omit it and it applies to everything.
appliesTo: nodes => nodes.filter(node => node.getData()?.type !== 'case'),
// The cheap call: what is out there, and what you could narrow by.
summarize: async (nodes, narrowing, { signal }) => {
const { total, byType } = await post('/api/correlations/count', {
ids: nodes.map(node => node.id),
types: narrowing.type ?? [], // pass the narrowing on so the count tracks it
}, signal)
return {
total, // judged against maxCandidates
facets: [{
key: 'type', // the key you read back out of `narrowing`
label: 'Type',
type: 'multiselect',
options: byType.map(row => ({ label: row.label, value: row.value, count: row.count })),
}],
}
},
// The expensive call: a flat fragment, run only on a number the analyst agreed to.
fetch: async (nodes, narrowing, { signal }) => {
const { items } = await post('/api/correlations', {
ids: nodes.map(node => node.id),
types: narrowing.type ?? [],
}, signal)
return {
nodes: items.map(item => ({
id: item.uuid, // an id already on canvas is skipped, never overwritten
data: { label: item.value, type: item.type, seen: item.first_seen },
})),
edges: items.map(item => ({
from: item.origin_uuid, // both endpoints must land for the edge to land
to: item.uuid,
data: { type: 'correlation' },
})),
}
},
// The write half, and the only optional one here. Omit it and this pivot's results
// are *not savable* — never counted unsaved, and no Save offered. See below.
async save({ origin, nodes, edges }, ctx) {
const written = await post('/api/correlations/save', {
event: origin[0]?.id,
nodes: nodes.map((node) => node.getData()),
}, ctx.signal)
return {
savedNodeIds: written.ok,
savedEdgeIds: edges.map((edge) => edge.id),
message: written.refused ? `${written.refused} refused by the server` : undefined,
}
},
}
new Pivotick(document.querySelector('#graph'), data, { pivots: [correlations] })Vier Dinge, bei denen man leicht etwas falsch machen kann und die alle oben sichtbar sind:
narrowinggehört Ihnen zu Ehren. Es kommt als{ [facetKey]: value }an, unter Verwendung der Schlüssel, die Ihre eigenen Facetten deklariert haben. Es zu ignorieren ist legal, aber dann bewegt sich die Zählung nie und ein Pivot über der Obergrenze kann nie entsperrt werden.signalist keine optionale Höflichkeit. Wenn Sie den Pivot-Modus verlassen oder den Ursprung ändern, werden Fragen im Flug abgebrochen. Geben Sie es an jede Anfrage weiter, oder Sie zahlen für Antworten, auf die niemand wartet.ctx.isStale()ist die gleiche Prüfung für Transporte, die kein Signal empfangen.- Auslösen bei einem Fehler, leere Rückgabe bei einem Leerlauf. Ein ausgelöster Fehler erscheint am Pivot-Eintrag mit einem Wiederholen.
{ total: 0 }ist ein ehrliches „Nichts da draußen“, und die beiden lesen sich für einen Analysten völlig anders. - Sagen Sie, wenn Sie es nicht wissen. Eine Quelle, die live gefragt wird, hat möglicherweise keine Zählung, bevor sie antwortet. Rückgabe
{ total: null, facets }: Es wird keine Zählung gezogen,maxCandidateslehnt sie nicht ab undcandidateCeilingbegrenzt immer noch, wasfetchzurückgibt.0würde behaupten, dass es nichts gibt. - IDs sind der Dedup-Schlüssel. Gibt die stabile ID des Quellsystems zurück, nicht eine pro Antwort, sonst landet jede Wiederholung erneut auf denselben Knoten als neue Kandidaten.
Der Anbietervertrag
interface PivotDefinition {
id: string // provenance tag, menu key, triage key
label: string // used verbatim, so it can be translated
icon?: string // trusted SVG, injected as-is
origin?: 'selection' | 'none'
appliesTo?: (nodes: Node[]) => boolean | Node[]
summarize?: (nodes: Node[], narrowing: PivotNarrowing, ctx: PivotContext) => PivotSummary | Promise<…>
fetch: (nodes: Node[], narrowing: PivotNarrowing, ctx: PivotContext) => PivotResult | Promise<…>
autoIngest?: boolean
maxCandidates?: number
save?: (payload: PivotSavePayload, ctx: PivotSaveContext) => PivotSaveOutcome | Promise<…>
autoSave?: boolean
}Beide Aufrufe benötigen ein Array von Knoten, sodass ein Pivot über fünfzig davon einer Backend-Anfrage statt fünfzig entspricht. Beide nehmen die aktuelle Verengung an. Beide erhalten ein PivotContext mit signal und isStale(), das gleiche Abbruchmuster, das jeder asynchrone Hook in der Bibliothek verwendet.
appliesTo: der gesamte Ursprung oder ein Teil davon
Geben Sie einen boolean für eine Regel über den Ursprung als Ganzes und die von Ihnen akzeptierten Knoten für eine Regel zurück, die jeweils einen Knoten liest. Ein leeres Array bedeutet dasselbe wie false.
appliesTo: nodes => nodes.length >= 2 // whole origin
appliesTo: nodes => nodes.filter(n => n.getData()?.type === 'domain') // per nodeDie Form pro Knoten ist wichtig, sobald eine Auswahl gemischt ist: Eine Domain und eine IP, die zusammen ausgewählt werden, behalten jeweils ihre eigenen Anbieter, anstatt nur diejenigen übrig zu lassen, die beide akzeptieren, und das Panel sagt dies im Eintrag – Gilt für 3 der 5 ausgewählten.
Was auch immer es behält, ist der Ursprung, mit dem der Anbieter aufgerufen wird: summarize und fetch sehen nie einen Knoten, den er abgelehnt hat, und die zwischengespeicherte Zusammenfassung wird durch diesen eingegrenzten Ursprung verschlüsselt. Es wird bei jeder Ursprungsänderung erneut gelesen. Halten Sie es daher synchron und kostengünstig.
graph.pivots.for(nodes) // everything that applies, partially included
graph.pivots.originFor('whois', nodes) // the share this one would run onDas Aufrufen von run mit einem Ursprung, den ein Pivot vollständig ablehnt, löst 'failed' auf, anstatt einen leeren Ursprung abzurufen – ein Lauf, der gut aussah und nichts beantwortete, wäre schlechter.
summarize: das Billigste
Vollständig dargestellt unter Ein Komplettanbieter. Seine total ist die Zahl, anhand derer die Kappe beurteilt wird, und seine facets wird zu den einschränkenden Steuerelementen: Sie deklarieren die Form, die Bibliothek zeichnet die Widgets. Geben Sie die Eingrenzung an Ihr Backend weiter, damit die Zählung die Auswahl des Analysten verfolgt, andernfalls kann sich das Tor nie öffnen.
Eine Facette kann eine default deklarieren: Der Optionswert a multiselect beginnt mit einem Häkchen, oder der Wert a select oder boolean beginnt bei. Das Panel zeichnet es und Eingrenzung löschen kehrt zu ihm zurück, aber der Schlüssel bleibt außerhalb der Eingrenzung, während er gehalten wird, sodass Ihr Anbieter einen fehlenden Schlüssel als Standard einlesen muss. Eine geleerte multiselect mit einem Standardwert wird als [] gesendet, was keine bedeutet.
{ key: 'element', label: 'Element', type: 'multiselect', default: ['attribute', 'object'],
options: [/* attribute, object, tag, … */] }Wenn Sie summarize vollständig weglassen, bietet der Pivot keine Zählung und keine Einschränkung, sondern nur eine Schaltfläche Ausführen. Das ist legal, und dann muss fetch sicher sein, blind zu callen, denn nichts kann es blockieren.
Zählungen haben beratenden Charakter. Stets.
summarize gibt einen Anspruch zurück, keinen Vertrag, und er unterscheidet sich zu Recht von dem, was Ingest landet: zuerst Dedup, dann Berechtigungen, Veraltung und Anbieter, die sich absichtlich annähern. Die Benutzeroberfläche markiert den Unterschied mit einer Tilde – ~2,143 correlations ist die Nummer des Anbieters, während 210 fetched und 12 selected die eigene Nummer der Bibliothek sind. Eine Verkleinerung ist niemals ein Fehler und eine Zählung ist niemals belastend für das Layout.
fetch: das Echte
Gibt ein flaches Fragment zurück: { nodes: RawNode[], edges: RawEdge[] }. Kein zurückgegebener Knoten benennt einen vorhandenen übergeordneten Knoten auf der Arbeitsfläche. Die Verschachtelung erfolgt in RawNode.children, sodass ein Container als neuer Knoten mit seinem eigenen Unterbaum ankommt. Ein nur Kanten-Ergebnis ist zulässig: Kanten, deren Endpunkte sich alle bereits auf der Arbeitsfläche befinden, werden zu eigenen Triage-Zeilen.
Facettentypen
Bei der Eingrenzung wird jeder Facettentyp verwendet, den die Abfrage-Engine kennt, mit Ausnahme von regex, für dessen Auswertung kein Backend aufgefordert werden kann: text, select, multiselect, numberRange, boolean. Regex befindet sich in den eigenen clientseitigen Filtern des Triage-Bereichs, wo die Zeilen bereits vorhanden sind.
Wie das Panel sie zeichnet und woher die Aufschlüsselungslinie kommt
Eine multiselect wird als Liste von Kontrollkästchen mit jeder Option und ihrer count auf dem Bildschirm angezeigt, sodass die Zahlen, die Sie eingrenzen, beim Eingrenzen sichtbar bleiben. Eine Single-Choice-Option select bleibt ein Dropdown-Menü und trägt seine Anzahl in den Optionsbeschriftungen.
Unter der Summe druckt das Panel, woraus es besteht, bezogen auf die erste multiselect-Facette: Diese Optionen unterteilen das Ergebnis. Sobald für diese Facette eine Auswahl vorliegt, entweder gesendet oder standardmäßig, werden nur die ausgewählten Optionen aufgelistet. Die Anzahlen einer Single-Choice-Facette sind Alternativen und keine Teile, daher werden sie dort nicht summiert.
Anschnitt: die Kappe und die Decke
maxCandidates: 2000 // per pivot: refuse to fetch while the count exceeds it
new Pivotick(el, data, { pivotCandidateCeiling: 10_000 }) // absolute, all pivotsBeide lehnen ab und weder Kürzungen noch Samples. Eine Ablehnung bringt immer drei Dinge mit sich: die Anzahl, die Grenze und den Weg nach vorne. Auf „Zu viele Ergebnisse“ ohne Zahl kann ein Analyst nicht reagieren.
Die Obergrenze wird anhand der neuesten Zählung für die aktuelle Verengung beurteilt, wodurch sie angehoben werden kann. Von 2.143 auf 210 eingrenzen und Fetch schaltet sich von selbst ein. Bei der Obergrenze geht es eher um den Speicher für Kandidatenobjekte als um die Tabelle, die virtualisiert wird.
Kandidaten sind nicht der Graph
Nichts, was ein Pivot zurückgibt, befindet sich im Diagramm, in der Datentabelle oder in einer Facettenanzahl, bis es erfasst wird. Für die Triage bereitgestellte Ergebnisse öffnen die Registerkarte Review des Docks – ein Bereich pro Pivot, aufgelistet als vertikale Registerkarten an der Seite, jede mit ihrer eigenen Anzahl und ihrem eigenen Ausgang – mit den eigenen Spalten des Anbieters, den Filtern der Datentabelle, Sortierung, Paginierung und einer Aufnahmeaktion.
- Die Ablehnung ist explizit und wird für die Sitzung gespeichert, eingegeben pro Pivot. Beim nächsten Durchlauf wird dieser Kandidat nicht erneut angeboten und in der Kopfzeile steht, wie viele unterdrückt wurden. Eine abgelehnte Reihe wird an Ort und Stelle durchgestrichen und nicht verschoben und bleibt reversibel.
- Was ein Pivot zurückhält, wird in seinem Eintrag im Pivot-Bedienfeld aufgeführt, benannt nach der Tabelle, die ihn benannt hat, mit restore in jeder Zeile und Restore all darunter. Es bleibt dort, sobald das Fenster geschlossen wird, sodass eine Ablehnung erneut geprüft werden kann, ohne den Pivot erneut ausführen zu müssen, um es zu finden.
- Knoten und Kanten sind separate Blöcke, jeder benannt und gezählt, wenn ein Lauf beide zurückgibt. Ein Nur-Kanten-Ergebnis ist eine eigene Tabelle und kein leerer Bereich.
- Eine Containerzeile wird geöffnet. Sie enthält eine Children-Anzahl dessen, was sie direkt enthält, und ein Caret listet den Inhalt auf, mit den gemeinsamen Attributen jedes untergeordneten Elements und einem Hinweis, wenn ein untergeordnetes Element selbst ein Container ist. Durch das Öffnen einer Zeile wird diese nicht markiert, und nichts darin kann einzeln ausgewählt werden – das Aufnehmen der Zeile beansprucht den gesamten Container.
- Durch das Schließen eines Anbieters wird nichts abgelehnt. Nicht geprüfte Reste kommen beim nächsten Durchlauf wieder zurück. Alle verbleibenden ablehnen ist die einzige Geste, die sie beseitigt.
- Eine erneute Ausführung ersetzt den Bereich dieses Anbieters. Wenn Sie Zeilen markiert haben, wird dies angezeigt und angeboten: Neu anzeigen / Triagieren Sie fort, anstatt Ihre Triage ungefragt wegzuwerfen.
- In eine Gruppe aufnehmen platziert die ausgewählten Zeilen gefaltet, eine Gruppe pro Typ, außerhalb des Ursprungs. Siehe Pivot-Landungen. Es wird angeboten, sofern
UI.simplifynicht ausgeschaltet ist. MitpivotIngestGrouped: truewird jede Landung gruppiert und auf der Schaltfläche steht stattdessen Ingest lose.
Alles, was der Bereich tut, ist auch programmgesteuert erreichbar:
graph.pivots.candidates('correlations') // the staged set
graph.pivots.mark('correlations', id) // …markAll / reject / rejectRemaining
await graph.pivots.ingest('correlations') // commit the marked ones
await graph.pivots.ingest('correlations', 'triage', { group: true }) // …folded, one group per type
graph.pivots.discard('correlations') // close it; rejects nothing
graph.pivots.rejectedRows('correlations') // what the session is holding back
graph.pivots.unreject('correlations', id) // …unrejectAll for every one of themautoIngest
Für ein kleines, vertrauenswürdiges Ergebnis, das niemand durchgehen möchte, z. B. das Erweitern eines Containers in sein Dutzend untergeordneter Elemente, deklarieren Sie autoIngest: true. Die Ergebnisse landen direkt und werden durch das Rückgängigmachen in der oberen Leiste wie bei jeder anderen Aufnahme wiederhergestellt. Dies entscheidet der Anbieter, da nur er weiß, ob seine eigenen Ergebnisse klein und vertrauenswürdig genug sind, um auf die Triage zu verzichten.
Es nicht eingestellt zu lassen ist nicht dasselbe wie false. Es heißt, dass der Pivot keine Ansicht hat, was einem Ein-Klick-Pivot die Freiheit lässt, allein über die Größe zu entscheiden; false sagt, dass Ergebnisse immer im Auge behalten werden müssen, und fühlt sich geehrt, egal, wie der Lauf gestartet wurde.
autoIngest: true // always lands
autoIngest: false // always reviewed
// unset: a one-click run decides on size
new Pivotick(el, data, { pivotQuickIngestLimit: 50 }) // that size, all pivotsAlle Länder, von hier oder von Review, können gefaltet in Landegruppen statt lose ankommen:
new Pivotick(el, data, { pivotIngestGrouped: true }) // every landing, one group per typeIngest und was es darf
Eine Geste, ein Stapel, ein dataBatchChanged, in dieser Reihenfolge:
- Dedup – eine ID, die sich bereits auf der Arbeitsfläche befindet, wird übersprungen und nie überschrieben, unabhängig davon, ob sie vorhanden war, als die Kandidaten inszeniert wurden, oder ob ein anderer Lauf sie während des Wartens landete.
- Kindervereinigung nach ID.
- Platzierung in der Nähe des Ursprungsknotens, zittert. Der eigene
x/yeines Anbieters gewinnt und ein Run ohne Ursprung setzt in der Mitte des Ansichtsfensters. - Provenienz, dann landet die Charge.
Ingest ist rein additiv. Die einzigen Dinge, die jemals entfernt werden, sind removeBySource und das Ausführen von „Rückgängig“.
Ein Tor, einmal aufgerufen mit dem ganzen Set:
callbacks: {
onBeforeIngest: async ({ pivotId, origin, candidates, trigger, confirm }) => {
if (candidates.nodes.length < 50) return true
return await confirm({ title: `Add ${candidates.nodes.length} nodes?` })
},
}Geben Sie false zurück, um ein Veto einzulegen, oder { accept: true, nodes, edges }, um eine Teilmenge zu landen. Ein weggelassener Schlüssel bedeutet „wie angefordert“, genau wie onBeforeDelete lautet.
Provenienz
Jeder Knoten und jede Kante trägt die Menge von Quellen, die dafür bürgen. Eine Menge und kein Skalar, da zwei überlappende Pivotpunkte auf einem Knoten der Normalfall sind. Bei einem Skalar löscht der zweite, der eintrifft, den Anspruch des ersten, und wenn einer von beiden entfernt wird, wird auch der Knoten mitgenommen.
node.getSources() // ['seed', 'correlations'], where 'seed' is the loaded data
node.hasSource('correlations')
edge.getSources() // edges carry it too, which is the load-bearing half
graph.removeBySource('correlations') // drop the tag; delete only when the set emptiesEinen Rücklauf machen
Ein Ingest ist ein Eintrag in graph.history, zusammen mit den anderen Dingen, die den Inhalt der Arbeitsfläche ändern: Löschungen, dauerhafte Ausblendungen und von Hand gezeichnete Elemente.
graph.history.entries() // newest first; a pivot entry's `id` is its runId
graph.history.undo() // the newest entry
graph.history.undo(runId) // that ingest, and contiguously anything done since
graph.history.redo() // re-lands the recorded delta; no refetch, no re-gatingRückgängig machen ist zusammenhängend: Wenn Sie auf einen älteren Eintrag zielen, werden auch alle darüber liegenden Einträge als ein Stapel rückgängig gemacht. Die alleinige Umkehrung eines alten Laufs ist graph.removeBySource(pivotId), wodurch die Bürgschaft dieser Quelle gelöscht wird und nur das gelöscht wird, wofür nichts anderes bürgt – eine Vorwärtsoperation, die als solche aufgezeichnet wird.
Eine teilweise Aufnahme aus einem bereitgestellten Satz erhält eine eigenerunId, sodass jeder Stapel ein separater Eintrag ist. Und das Rückgängigmachen einer Aufnahme ist immer noch der neueste Eintragfügt seine Kandidaten wieder in den Überprüfungsbereich einanstatt den Anbieter noch einmal danach zu fragen. SehenRückgängig machen und Verlauffür das Ganze.
Ergebnisse speichern
Ingest bringt Kandidaten auf die Arbeitsfläche; es schreibt sie nirgendwo hin. Ein Pivot, der save deklariert, kann seine eigenen Ergebnisse zurück in das System übertragen, aus dem sie stammen, und die Bibliothek führt das Hauptbuch: welche Elemente ein Lauf erstellt hat, ob sie geschrieben wurden, welche fehlgeschlagen sind und was ein Wiederholungsversuch enthalten sollte.
Lassen Sie save weg und die Pivot-Ergebnisse sind nicht speicherbar. Sie gelangen nie in das Hauptbuch, werden nie ungespeichert gezählt und für sie wird keine Speicherung angezeigt. Das ist die richtige Deklaration für einen Pivot über abgeleitete Daten – Sie können die Ausgabe einer Korrelations-Engine nicht zurückschreiben – und sie verhindert, dass dauerhaft „210 nicht gespeichert“ ohne Abhilfe angezeigt wird.
Das Ganze, einschließlich teilweiser Schreibvorgänge, Wiederholungsversuche, der IDs, die ein Quellsystem prägt, und was durch Rückgängigmachen erreicht wird und was nicht: Pivot-Ergebnisse werden gespeichert.
Der Pivot-Rail-Modus
Mit einem geht man über die Oberfläche. Was zählt, wenn Sie ihn konfigurieren, statt ihn zu steuern: Der Eintritt in den Modus ist die Absicht, die einen Anbieter aufruft – ebenso wie das Öffnen von Pivot ▸ im Kontextmenü – wenn Sie ihn verlassen, wird jede Frage im Flug gestoppt, und die Auswahl allein kostet nichts. Die Auswahl ist auch die Geste zum Ziehen, Stylen und Massenbearbeiten, sodass die Bibliothek niemals spekulativ einen Anbieter aufruft.
Der Modus ist in der Registrierung gated:
UI: { pivotMode: 'auto' } // default: the button exists only while a pivot is registered
UI: { pivotMode: true } // force it, for pivots that arrive asynchronously
UI: { pivotMode: false } // neverEin Verbraucher, der keine Pivots registriert, sieht daher keine Spur von der Funktion. Es wird niemals im Modus viewer oder static gemountet.
Ursprungslose Pivots
Einige Anreicherungen verfügen über keinen Knoten, auf dem sie ausgeführt werden können, z. B. zum Suchen, Importieren oder Einfügen einer Liste von Indikatoren. Deklarieren Sie origin: 'none' und der Pivot erhält [], wird angewendet, wenn der Ursprung leer ist, und faltet sich in eine Ohne Ursprung-Gruppe, sobald eine ausgewählt wird. Die Ergebnisse werden durch denselben Triage-Bereich, dasselbe Gate und dasselbe Provenienzmodell geleitet und landen in der Mitte des Ansichtsfensters.
appliesTo ist für eines davon bedeutungslos und wird nicht herangezogen.
Erklärtes Potenzial
Ein Hinweis am Rand des Knotens mit der Aufschrift „Hier gibt es 2.100 Korrelationen“, der von demjenigen platziert wurde, der die Daten geladen hat.
node.setPotential('correlations', 2100) // 0 clears it
node.getPotential('correlations')
node.setPotential(2199) // the total across every pivot
node.getPotential()Es ist deklariert und wird nie abgefragt. Die Bibliothek ruft keinen Anbieter an, um ein Abzeichen zu zeichnen, da eine Zählung, die in dem Moment, in dem nach ihr gefragt wurde, auf einem Knoten eintrifft, dazu führen würde, dass auf der Arbeitsfläche zwei verschiedene Geschichten erzählt werden. Wenn Sie auf das Abzeichen klicken, wird der Pivot-Modus für diesen Pivot geöffnet.
Wie jeder andere Setter auf Node markiert er den Knoten als schmutzig: Rufen Sie graph.renderer.update() auf, wenn nichts anderes gerendert werden soll.
Was die Felge zeichnet
Standardmäßig werden die Felgenabzeichen nur angezeigt, wenn der Pivot-Modus geöffnet ist, wo sie bei der Auswahl eines Ursprungs helfen; Wenn Sie dort auf einen klicken, wird das Panel auf diesen Knoten und Pivot beschränkt. pivotRimBadgeVisible ändert sich, wenn Folgendes angezeigt wird:
new Pivotick(el, data, { pivots, pivotRimBadgeVisible: 'always' })
graph.pivots.rimBadgeVisible = 'pivot-mode' // or later; it dirties every node'pivot-mode' (Standard) | Nur solange der Pivot-Rail-Modus geöffnet ist. |
'always' | Unabhängig vom Modus ist ein Badge auch ein Weg in den Pivot-Modus. |
'never' | Überhaupt nicht. |
Sobald sie gezeigt werden, ist ihre Form die nächste Wahl.
Ein Knoten hat vier Randecken und nur zwei, sobald er untergeordnete Knoten hat – das Erweiterungsangebot reserviert die Ostseite. Ein Ausweis pro Anbieter ist also bei einer Handvoll lesbar und bei hundert unmöglich. pivotRimBadge wählt die Form:
new Pivotick(el, data, { pivots, pivotRimBadge: 'summary' })
graph.pivots.rimBadge = 'per-pivot' // or later; it dirties every node'per-pivot' (Standard) | Ein Abzeichen pro Pivot, der ein Potenzial deklariert, jedes Öffnen dieses Pivots. Einige von ihnen kollabieren schon früh auf einem Knoten mit Kindern zu einem +n. |
'summary' | Ein Badge, unabhängig vom Anbieter. Es zeigt die deklarierte Gesamtzahl ohne Pivot-ID oder – ohne Deklaration – wie viele Pivots gelten. |
'off' | Nichts. NodeStyle.badges zeichnet immer noch. |
Was ein summary-Abzeichen bewirkt, wenn es angeklickt wird, und woher seine Anzahl kommt
In summary ist das Abzeichen eine Geste: Es öffnet das Pivot-Panel auf diesem Knoten, da bei jeder echten Anbieterzählung das Panel der Ort ist, an dem die Zählungen und die Auswahl leben. Die Ausnahme ist ein Knoten, bei dem genau ein Pivot * und * anwendet. Er deklariert Nein summarize – nichts, was zuerst gelesen werden muss, und nichts, was eingegrenzt werden muss –, wo der Klick es ausführt. Wo das Ergebnis landet, ist der eigene Aufruf des Anbieters: autoIngest entscheidet wie überall zwischen Canvas und Triage-Bereich.
Die Anzahl der angewendeten Pivots wird abgeleitet und nicht deklariert, sodass kein Anbieteranruf anfällt. Es wird zwischen Diagrammänderungen gehalten und aktualisiert, wenn die Registrierung oder die Knoten des Diagramms verschoben werden. Eine appliesTo-Lesung von etwas anderem kann am Rand zurückbleiben, und das Panel ist immer die genaue Antwort.
Caching
Die Bibliothek speichert eines zwischen: summarize Ergebnisse, verschlüsselt nach (Pivot, Ursprung, Eingrenzung), da eine Mehrfachauswahlzusammenfassung ein Aggregat ist, das durch nichts zerlegt werden kann. Alles andere gehört dir:
graph.pivots.invalidate() // all of it
graph.pivots.invalidate('correlations') // one pivot's
graph.pivots.invalidate('correlations', nodes) // only what was asked about theseAuthentifizierung, Wiederholung, Ratenbegrenzung und jeder eigene Cache gehören zu den Funktionen Ihres Anbieters, bei denen es sich um einfache Funktionen handelt, die Sie geschrieben haben.
Was Ihre Quelle zu bieten hat
Die oben genannten Seiten richten sich an denjenigen, der den Anbieter schreibt. Dies ist derselbe Vertrag, der von der anderen Seite gelesen wird, und zwar für denjenigen, der den aufgerufenen Dienst besitzt.
- Ein Abrufaufruf, der die Objekte und ihre Beziehungen zurückgibt. Das einzig Erforderliche. Dabei sind zwei Dinge wichtig: Es benötigt viele IDs in einer Anfrage, sodass ein Pivot über fünfzig Knoten einen Aufruf kostet, und die zurückgegebenen IDs sind stabil über alle Läufe hinweg, weil dies der Grund für die Deduplizierung von Schlüsseln ist. Eine Pro-Antwort-ID landet die gestrigen Ergebnisse erneut als neue Kandidaten.
- Ein Zählaufruf, der dieselben IDs und dieselben Filter verwendet und eine Gesamtsumme und die Anzahl pro Wert für alles zurückgibt, durch das eingegrenzt werden kann. Es sorgt dafür, dass das Tor und die Verengung funktionieren, und es wird bei jeder Änderung neu abgefragt, daher muss es billig sein. Ungefähr ist in Ordnung: Zählungen sind beratend. Ohne einen läuft ein Pivot immer noch, mit einem bloßen Run und nichts, um ihn zu steuern.
- Die gleichen Filter werden von beiden berücksichtigt. Eine Zählung, die sie ignoriert, bewegt sich nie, sodass ein Pivot über seiner Obergrenze niemals entsperrt werden kann.
- Eine Möglichkeit zum Abbrechen. Fragen werden aufgegeben, wenn der Analyst weitergeht, und die
signal, die dem Anbieter übergeben werden, sind nur dann weiterzugeben wert, wenn der Transport darauf einwirkt. - Ein Schreibanruf, wenn die Ergebnisse Ihnen gehören, können Sie zurückschreiben. Es sollte die zugewiesenen IDs zurückgeben, damit
canonicalIdsverhindern kann, dass der morgige Lauf das Dupliziert, was gespeichert wurde, und einen Teilerfolg melden kann.
Beide Leseaufrufe sollten abgesehen von einem Fehler eine leere Antwort liefern: Keine Ergebnisse sind eine Tatsache über die Daten, ein Fehler ist eine Tatsache über den Dienst, und sie lesen sich für einen Analysten völlig anders.
Hier ist keine Paginierung, ein Cursor oder ein Streaming-Transport erforderlich. Sie werden durch eine Verengung ersetzt.
Was das nicht ist
- Kein Streaming und kein Server-Cursor. Eine Eingrenzung ersetzt sie: Anstatt zweitausend Zeilen abzurufen, fragen Sie, was da ist, und lassen Sie den Analysten auswählen. Der Vertrag erfordert niemals ein Streaming-Backend, da viele Quellen keins haben.
- Keine Neuzuordnung. Ein zurückgegebener Knoten benennt niemals einen vorhandenen übergeordneten Knoten. Die Vereinigung von Kindern nach ID ist die einzige Möglichkeit, wie die Aufnahme die untergeordneten Elemente eines vorhandenen Knotens berührt.
- Kein Rückgängigmachen der Eigenschaftsbearbeitung.
graph.historydeckt ab, was die Arbeitsfläche enthält und zeigt – was eingegangen ist, was ausgegangen ist, was verborgen ist. Die Daten eines Knotens befinden sich im Backend-Status, den die Bibliothek nicht erstellt hat. Das lokale Zurücksetzen eines Felds ist also die Aufgabe vononBeforeNodeEditCommit, nicht unsere. - Keine Transportrichtlinie.
saveschreibt zurück, aber Anmeldeinformationen, Wiederholungsversuche, Ratenbegrenzung und Konflikterkennung liegen ausschließlich beim Verbraucher. Der einzige Wiederholungsversuch der Bibliothek besteht darin, dass der Analyst auf Wiederholen drückt. - Kein Ablehnungsspeicher während eines Neuladens. Eine Ablehnung ist ein Urteil im Kontext einer Sitzung, und derselbe Kandidat kann in einem anderen Diagramm eine andere Antwort wert sein. Ablehnungen gelten für die Sitzung und nicht länger.
- Keine Aktualisierung dessen, was bereits hier ist. Ein Kandidat mit übereinstimmender ID lässt den vorhandenen Knoten unberührt; Für den Abgleich eines Knotens mit einer neueren Kopie ist eine Konfliktrichtlinie erforderlich, über die die Bibliothek nicht verfügt.