ピボットとエンリッチメント
pivot は実行可能なエンリッチメントです。「これに関してソースが他に何を示しているかを示してください」。あなた それを宣言すると、ライブラリがそれを実行し、トリアージのために結果をステージングし、それらを アナリストがそう言うと、すべての要素の出所をグラフに表示します。
これにより解決される問題は、通常、対象となるノードの接続が多すぎるということです。あ 単一の指標が他の何千もの指標に関連付けられる可能性があります。したがって、ピボットは 2 回の呼び出しです。 1 つは、そこにあるものを伝える安価な通話と、次の日にのみ実行される高価な通話です。 アナリストが同意した数値。
以下のどこでも一貫して使用される語彙
- pivot: 実行可能なエンリッチメント
- provider: 提供する 2 つの関数
- 候補: 取得された結果、まだグラフに表示されていません
- ingest: 選択した候補をグラフにコミットします
- reject: セッション中に記憶される明示的な却下
- source: ピボットが着地するすべてのものに書かれた来歴タグ
- save: 取り込まれた結果を元のシステムに書き戻す out
- 未保存: キャンバス上、まだ書き込まれていない — 推定ではなく正確な数
summarize → fetch → triage → ingest → save
"2,143" the 210 the 12 on canvas in the source systemfetch のみが必要です。その行にある他のすべては、オプトインするステップです。
動作を確認します: ピボット&エンリッチ フローの絞り込み、およびトリアージのための拒否と再試行 そして故障状態。
1 を使用することは、アナリストが順番に行うことと、各ステップのコストです。 バックエンド。 登録以降は全て参考になります。 誰がそれを接続するか、そしてあなたのソースが提供するもの サービス側から読み取ったものと同じコントラクトです。
1 つを使用する
1 · ピボット モードに入ります。 P を押すか、左側のレールの ピボット スロットをクリックします。 スロットは、少なくとも 1 つのピボットが登録されている場合にのみ存在します。何も求められていません バックエンドはまだであり、モードが閉じている間にノードを選択しても何も要求されません。ボックス選択 50 個のノードをドラッグしても、50 カウントのクエリが起動されるべきではありません。
2 · 起点を選択します。 起点は、質問の対象となるノード セットです。 原点の選択は 静止ツールなので、単純にクリックするだけで設定されます。 なげなわ原点はいくつかの周りを描きます。何でも モードに入ったときにすでに選択されていた が原点となり、クリアで空になります。 空のオリジンにはまだ使用法があります: 宣言されたピボット origin: 'none' が適用されます。 何も選ばれていないとき。
3 · 該当するものをお読みください。 モードに入ることが、summarize を 1 回につき 1 回実行するインテントです。 該当するピボットは、マルチノードのオリジンに対する単一の呼び出しにバッチ化されます。各エントリは何を示していますか そのプロバイダーの主張はそこにあり、~2,143 と書かれています。チルダはプロバイダーの番号を示します ライブラリがカウントする 1 つではなく、アサートされます。 summarize のないピボットは裸の状態を示します 代わりに実行を実行してください。何も該当しない場合は、空のリストが表示されるのではなく、パネルにその旨が表示されます。
4 · ゲートが開くまで狭い。 maxCandidates を宣言するピボットは、** Fetch** を維持する カウントがそれを超えている間は手の届かないところにあり、その数、制限、方法を示します 前へ。絞り込みコントロールにチェックを入れると、その選択で summarize が再質問されるため、2,143 になります。 210 と Fetch が自動的にオンになります。 狭まりをクリアは最初からやり直します。その横に、Fetch & ingest は、レビューせずにすべての結果をグラフに表示します。ピボットする場所でのみ提供されます autoIngest は未設定のままです。
5 · 返されたものをトリアージします。 結果はグラフに触れません。 レビュー タブを開きます 下部ドックでは、実行したプロバイダーごとに 1 つのペインが横にリストされます。どこでもクリック 行上でマークを付け、Shift キーを押しながらクリックして範囲全体にマークを移動し、一致する n 個をすべて選択 現在のフィルターを尊重します。 検索行… フィルター (正規表現はオプション) と列の並べ替え。 選択を拒否 および 残りすべてを拒否 行を明示的に破棄し、記憶されます × は単にペインを閉じるだけで、何も拒否しません。 再実行が再度質問します 同じ狭まり方で。ストリップの下部で、Close failed がすべてのフェッチを削除します。 何も戻さず、すべて閉じる でキューを空にします。どちらも何も拒否しません。 候補はグラフではありませんには残りがあります。
6 · 取り込み、間違っていた場合は元に戻します。 選択した取り込み (12) はそれらを正確にコミットします 12 個、ピボット元のノードの周囲に配置され、ソースとしてピボットのタグが付けられます。 すべての 213 を取り込み、マーキングをスキップし、プロバイダーがまだ残っているすべてをコミットします テーブルがフィルタリングされているものは何でも。乾杯の音符にはIngested 12 nodes, 14 edgesと書かれています。 元に戻すには、上部バーにある元に戻すコントロールを使用します。これは、一時的にではなく永続的に存在します。 トーストが続く限り、実行全体 (そのエッジとマージされた子) が逆転します。 含まれています。
7 · ピボットで保存できる場合は、元に戻します。 取り込みにより、12 がキャンバスに配置されます。そうではありません どこにでも書いてください。ピボット パネルの下部には、このセッションに含まれる要素の数が表示されます 取り込まれ、書き戻されず、保存 がそれらを書き込みます。 save をノーと宣言するピボット この数値に寄与することはありません。「ピボット結果の保存」を参照してください。
何もルールを残さない取り込みはペインを閉じ、ストリップは次のペインに進みます。 プロバイダーが待機中です。残り物は開いたままにしており、その中で待っている再実行も同様です。最後に一度 ペインが移動すると、レビューが開いたドックが元の場所に折りたたまれるため、ピボットがレイアウトから離れます それは見つかりました。
ワンクリックピボット
ステップ 2 ~ 4 は、最初に読む価値のある質問です。ほとんどはそうではありません。ノードを右クリックし、 Pivot ▸ は、それに適用されるすべてのピボットと、それぞれが宣伝している内容をリストします。 行を選択すると、そこに行が実行されます。パネルはなく、入力するものはありません。原点は クリックされたノードがそのノードに属している場合は選択し、それ以外の場合はそのノードのみを選択するため、一括ピボットが実行されます。 メニューから離れることなくアクセスできます。
サブメニューを開くことも求められます。すべてのピボットに summarize はパネル自身の質問であり、この起源は 何も狭まらず、その行はカウントが到着するまでプレースホルダーを保持します: ~2,143、勧告 プロバイダーが宣伝するすべての番号と同様に。何も宣伝しないピボットは何も表示せず、 全く同じように動作します。質問はサブメニューをしばらく押し続けると消えますので、 他の場所へ向かう途中で Pivot ▸ を横切るポインタは何も尋ねず、次の場合にキャンセルされます。 それが最初に閉じます。回答は両方の方法でキャッシュされます。2 番目のルックが一度にペイントされ、パネルが表示されます。 メニューで学習した内容を再利用します。メニューよりも長いレジストリはスクロールを保持でき、行の途中でカットされます したがって、継続していると読み取れます。
結果がどこに表示されるかは、キャンバスに初めて追加された結果が何個あるかによって異なります。
- 最大
pivotQuickIngestLimitの新しい候補者 (デフォルトでは 50) が直接着陸し、タグ付けされ、 他の取り込みと同様に取り消し可能です。 - それ以上 の場合は、パネル実行とまったく同じように、代わりに「Review」タブを開きます。
- キャップ拒否 は何かがフェッチされる前に発生するため、レビューするものはありません。の ピボット パネルは、番号とそれを持ち上げる狭まりのあるピボット上で開きます。
何も絞られずに列が走ります。 1 つのピボットが実際には複数の実行である場合、アナリストは選択します オブジェクトの属性の 1 つを使用してオブジェクトを強化するなどの間では、 menuChoices を宣言します。 次に、その行は選択肢ごとに 1 行のサブメニューを開き、それぞれが独自のピボットを実行します。 狭くなり、最後の列がそのピボットでパネルを開きます。同期してそれぞれ実行されます メニューが開く時刻 (appliesTo など)。選択肢からのキャップ拒否により、パネルが開きます。 選択肢が狭まります。プレーン行の場合は [] を返します。
menuChoices: ([object]) => attributesOf(object).map((a) => ({
label: `${a.type}: ${a.value}`,
narrowing: { attribute: [a.id] },
})),すでにキャンバス上にある候補は新しいものではなく、制限を満たすことはないため、ピボットを再実行します カバーした地面はワンクリックのジェスチャーのままです。すでに画面上にある 2 つのノード間のエッジ これは、そうでなければ尋ねられるはずの行であるため、重要です。ピボット 宣言された autoIngest はすでに質問に答えており、その答えは次のとおりです。 true は常に着陸し、false は常にレビューします。制限は、それを離れたピボットに対してのみ決定します このジェスチャに対してのみ設定を解除します。パネル Fetch は常にステージングされます。
答えがわかるまでは何も表示されないため、画面を表示する必要はありません。 それは機能しています。 Review タブは、フェッチ中にドックのストリップ読み取り値 Review… に加わります。 ドックを展開したり、ペインを前に持ってきたりせずに、外に出すことができます。クリックして走行を視聴するか、 キャンセルしてください。決定する必要がある結果だけが前に出て、着地したランがその結果をもたらします。 それをタブで削除します。決定するものが何もない 2 つの答えは通知側のものです: a failed fetch 再試行と絶対値を超える結果でそう言います。 candidateCeiling は数値と制限を示します。
3 つの方法: レール モード、ノードのコンテキスト メニュー Pivot ▸ サブメニュー (不在、なし) 何も適用されない場合は無効になります)、および pivotRimBadgeVisible: 'always' では、 リム バッジ。独自のピボットをスコープとするモードを開きます。または、リムにバッジが 1 つ付いています。 それらすべてをノードに送信します。ノードのバッジ 絞り込むものが何もないピボットが 1 つだけある場合は、メニュー行のように直接実行されます。
1つを登録する
3 つのドア、1 つのレジストリを、アクセスする順序で示します。
// 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 はレジストリとランタイムの両方です。 for()、run()、undo()、 invalidate()と候補モデルはすべてそれにぶら下がっています。
完全なプロバイダー
機能するピボットに必要なものがすべて揃っており、何も取り残されることはありません。このページの残りの部分は、 各作品のリファレンス。これは最初にコピーして切り詰めるものです。
以下の post は独自の JSON ヘルパーです。これは非 2xx をスローし、signal を通過させます。 ピボットは HTTP ではなくコントラクトとして解釈されます。
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] })間違いやすい 4 つの点。すべて上に表示されています。
narrowingはあなたのものです。 キーを使用すると、{ [facetKey]: value }として到着します あなた自身の側面を宣言します。無視することは合法ですが、カウントは決して動かず、ピボットが発生します。 キャップを超えるとブロックを解除することはできません。signalは省略可能な丁寧さではありません。 ピボット モードを終了するか、原点を変更すると中止されます。 飛行中の質問。すべてのリクエストにそれを渡すか、誰も待っていない回答に対して料金を支払います のために。ctx.isStale()は、信号を受け取らないトランスポートの同じチェックです。- 失敗の場合はスロー、空の場合は空を返します。 スローされたエラーがピボットに表示されます。 再試行 でエントリーします。
{ total: 0 }は正直なところ「何もない」であり、2 人は次のように読みます。 アナリストとは全く違います。 - 分からないときは言ってください。 ライブで質問された情報源は、答えるまでカウントできない場合があります。 リターン
{ total: null, facets }: カウントは引かれず、maxCandidatesはそれを拒否しません、 そして、candidateCeilingは依然としてfetchが返すものを制限します。0は何もないと主張するだろう。 - ID は重複排除キーです。 応答ごとの ID ではなく、ソース システムの安定した ID を返します。 または、再実行するたびに、新しい候補として同じノードが再び表示されます。
プロバイダ契約は
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
}どちらの呼び出しもノードの 配列 を使用するため、そのうち 50 個を超えるピボットが 1 つのバックエンド リクエストになります。 50ではなく。どちらも現在の縮小を採用します。どちらも PivotContext のキャリーを取得します signal と isStale()、ライブラリ内のすべての非同期フックが使用する同じキャンセル パターン。
appliesTo: 起源のすべて、またはその一部
原点全体に関するルールの ブール値 と、受け入れられる ノード を返します。 一度に 1 つのノードを読み取るルール。空の配列は false と同じ意味です。
appliesTo: nodes => nodes.length >= 2 // whole origin
appliesTo: nodes => nodes.filter(n => n.getData()?.type === 'domain') // per nodeノードごとの形式は、選択が混合されるとすぐに重要になります (ドメインと IP が一緒に選択される)。 両方を受け入れるプロバイダーだけを残すのではなく、それぞれが独自のプロバイダーを維持します。 パネルにはエントリにそのように記載されています — 選択された 5 つのうち 3 つに適用されます。
保持されているものは、プロバイダーが呼び出されるオリジンです: summarize と fetch は決して表示されません 拒否されたノードと、キャッシュされた概要はその絞り込まれた起点によってキー設定されます。再読です オリジンが変更されるたびに実行されるため、同期を維持し、コストを抑えます。
graph.pivots.for(nodes) // everything that applies, partially included
graph.pivots.originFor('whois', nodes) // the share this one would run onピボットが拒否するオリジンを指定して run を呼び出すと、取得するのではなく 'failed' が完全に解決されます 空のオリジンでは、問題なく実行され、何も応答しなかった場合は、さらに悪い結果になります。
summarize: 安いもの
完全なプロバイダー に完全に表示されます。その total は、 キャップが判定され、その facets が絞り込みコントロールになります: 形状を宣言します。 ライブラリはウィジェットを描画します。ナローニングをバックエンドに渡すと、カウントが何を追跡するかがわかります。 アナリストはそう選択し、そうでなければゲートは決して上がりません。
ファセットは default を宣言できます: オプション値 a multiselect はチェックマークが付いているもので始まります。 select または boolean の値が始まります。パネルが引いて絞りをクリアに戻ります ただし、キーが保持されている間はキーが狭められないため、プロバイダーは デフォルトではキーがありません。デフォルトで空の multiselect は [] として送信されます。 なし を意味します。
{ key: 'element', label: 'Element', type: 'multiselect', default: ['attribute', 'object'],
options: [/* attribute, object, tag, … */] }summarize を完全に省略すると、ピボットはカウントや絞り込みを行わず、Run のみを提供します。 ボタン。それは合法であり、その場合、fetch は盲目と呼んでも安全でなければなりません。なぜなら、何もゲートできないからです。 それ。
カウントは参考値です。いつも。
summarize は契約ではなくクレームを返し、取り込んだものとは正当に異なります。 土地: 最初に重複排除、次にアクセス許可、失効性、および意図的に近似したプロバイダー。 UI は違いをチルダでマークします。~2,143 correlations はプロバイダーの番号です。 一方、210 fetched と 12 selected は図書館独自のものです。縮小は決してエラーではありません。 count はレイアウトに耐荷重を与えることはありません。
fetch: 本物
フラット フラグメント: { nodes: RawNode[], edges: RawEdge[] } を返します。返されたノード名はありません。 キャンバス上の既存の親。ネストは RawNode.children で移動するため、コンテナは 独自のサブツリーを持つ新しいノード。 エッジのみ の結果は正当です: エンドポイントが含まれるエッジ すべてすでにキャンバス上にあり、独自のトリアージ行になります。
ファセットの種類
絞り込みでは、バックエンドが使用できない regex を除く、クエリ エンジンが認識しているすべてのファセット タイプが使用されます。 text、select、multiselect、numberRange、boolean の評価を求められます。正規表現は生きています 優先順位ペイン独自のクライアント側フィルターでは、行はすでに手元にあります。
パネルがそれらをどのように描画するか、および内訳線はどこから来たのか
multiselect は、すべてのオプションとその count を含むチェックボックスのリストとして画面上に描画されます。 そのため、絞り込んでいる数値は絞り込んでいる間も表示されたままになります。単一選択 select ドロップダウンのままで、その数がオプション ラベルに表示されます。
合計の下に、パネルには最初の multiselect から取られた内容が印刷されます。 ファセット: これらのオプションは結果を分割します。そのファセットが選択されると、送信されるか、デフォルトで、 選択されたオプションのみがリストされます。単一選択ファセットのカウントは代替です 部分ではなく、そこで合計されません。
ゲート: キャップと天井
maxCandidates: 2000 // per pivot: refuse to fetch while the count exceeds it
new Pivotick(el, data, { pivotCandidateCeiling: 10_000 }) // absolute, all pivotsどちらも拒否し、切り捨てもサンプルも行いません。拒否には常に 3 つのことが含まれます 順序: 数、制限、および今後の方向。数字のない「結果が多すぎる」ことはありません アナリストが行動できるもの。
キャップは、現在のナローイングの最も新しいカウントに基づいて判断されます。 持ち上げます。 2,143 から 210 に絞り込むと、Fetch が自動的にオンになります。天井は記憶に関するものです 仮想化するテーブルについてではなく、候補オブジェクトについてです。
候補はグラフではありません
ピボットが返すものは、ピボットが返されるまで、グラフ、データ テーブル、またはファセット カウントには存在しません。 摂取されます。トリアージ用にステージングされた結果は、ドックの「Review」タブを開きます — ピボットごとに 1 つのペインがあり、垂直方向にリストされます タブはその横にあり、それぞれに独自のカウントと独自の出口があり、プロバイダー独自の方法が付いています。 列、データテーブルのフィルター、並べ替え、ページング、取り込みアクション。
- 拒否は明示的であり、ピボットごとにキー設定され、セッション中に記憶されます。次の走行 はその候補者を再度提示することはなく、ヘッダー行にはその候補者が何人を抑制したかが示されます。あ 拒否された行は移動されるのではなく、その場で取り消し線が引かれ、元に戻すことができます。
- ピボットが抑制しているものは、ピボット パネルのエントリにリストされ、 テーブルにはその名前が付けられ、各行に restore が付けられ、その下に Restore all が付けられます。そこに留まる ペインが閉じられると、ピボットを実行せずに拒否を再検討できます。 もう一度見つけるために。
- ノードとエッジは別個のブロック であり、それぞれに名前が付けられ、実行で両方が返されたときにカウントされます。 エッジのみの結果は、空のペインではなく、それ自体のテーブルです。
- コンテナの行が開きます。 直接保持しているものの数が Children となります。 キャレットは、各子の共有属性と子がどこにいるかというメモを含む内容をリストします。 それ自体がコンテナです。行を開くことはその行にマークを付けているわけではないので、行の中にあるものを選択することはできません。 独自 — 行を取り込むとコンテナ全体が使用されます。
- プロバイダーを閉じても何も拒否されません。 優先順位付けされていない残り物は、次回の実行時に戻ります。 残っているものをすべて拒否する は、それらを処分する 1 つのジェスチャーです。
- 再実行すると、そのプロバイダーのペインが置き換えられます。 マークされた行がある場合は、その旨が表示され、オファーが表示されます。 新しいものを表示 / トリアージを無断で放棄するのではなく、トリアージを続けてください。
- グループに取り込む は、選択した行を折り畳んで、タイプごとに 1 つのグループとして原点から外して配置します。 ピボット着陸を参照してください。
UI.simplifyでない限り提供されます。 オフになりました。pivotIngestGrouped: trueでは、すべての着地がグループ化され、ボタン 代わりに、Ingestルースと表示されます。
ペインが行うことはすべてプログラムからもアクセス可能です。
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
コンテナの拡張など、誰も選びたくない小規模で信頼できる結果の場合 その十数の子に、autoIngest: true を宣言します。結果は直接表示され、上部のバーの undo は、他の取り込みと同様にそれらを元に戻します。プロバイダーだけが知っているため、これはプロバイダーが決定します それ自体の結果が小さく、トリアージをスキップできるほど十分に信頼できるかどうか。
未設定のままにすることは、false と同じではありません。ピボットにはビューがないと表示され、 ワンクリックピボットサイズだけを自由に決定できます。 false が結果を言う 常に彼らに目を向ける必要があり、実行が開始されたことを光栄に思います。
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 pivotsここから、またはレビューからの土地が何であれ、折り畳まれて到着する可能性があります。 着陸グループ 緩いのではなく:
new Pivotick(el, data, { pivotIngestGrouped: true }) // every landing, one group per type摂取とそれが許可されていること
1 つのジェスチャ、1 つのバッチ、1 つの dataBatchChanged の順序:
- Dedup — すでにキャンバス上にある ID は、そこにあったかどうかに関係なくスキップされ、上書きされません。 候補者がステージに上がったとき、または待っている間に別のランがヒットしたとき。
- ID による子結合
- 起点ノード近くの配置、ジッターあり。プロバイダー自身の
x/yが勝ち、 ビューポートの中心にある原点のない実行シード。 - Provenance、その後バッチが着陸します。
取り込みは純粋に追加的です。削除されるのは removeBySource と run だけです 元に戻す。
セット全体で 1 回呼び出される 1 つのゲート:
callbacks: {
onBeforeIngest: async ({ pivotId, origin, candidates, trigger, confirm }) => {
if (candidates.nodes.length < 50) return true
return await confirm({ title: `Add ${candidates.nodes.length} nodes?` })
},
}拒否するには false を返し、サブセットを着陸させるには { accept: true, nodes, edges } を返します。省略されたキー onBeforeDelete の読みどおり、「要求どおり」を意味します。
来歴
すべてのノードとエッジは、それを保証するソースの set を保持します。ではなくセット スカラー。1 つのノード上で 2 つのピボットがオーバーラップするのが通常のケースであるためです。スカラーの場合、 2 番目に到着したノードは最初のノードの要求を消去し、どちらかを削除するとノードは次のようになります。 それ。
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 emptiesランニングバック
インジェストは、内容を変更する他のものと並んで、graph.history の 1 つのエントリです。 キャンバスには、削除、耐久性のある皮、手で描かれた要素が含まれます。
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-gating元に戻すは 連続 です: 古いエントリを目指すと、その上のすべてのエントリも元に戻ります。 1つのバッチ。古いランを 1 つだけ逆転すると graph.removeBySource(pivotId) となり、ドロップします。 そのソースが保証し、他に保証されていないもののみを削除します。つまり、転送操作です。 1つとして記録されます。
1 つのステージングされたセットからの部分的な取り込みは独自の runId を取得するため、各バッチは個別になります エントリー。そして、まだ最新のエントリ ** である取り込みを取り消すと、その候補が元のエントリに戻ります。 プロバイダーに再度質問するのではなく、[レビュー] ペイン** を参照してください。参照 元に戻すと履歴 をすべて実行します。
結果の保存
インジェストは候補をキャンバスに配置します。どこにも書きません。宣言するピボット save は自身の結果を元のシステムにプッシュし戻すことができ、ライブラリはその結果を保持します。 台帳: 実行によってどの要素が作成されたか、それらが書き込まれたかどうか、どの要素が失敗したか、および 再試行が何を運ぶべきか。
save を省略すると、ピボットの結果は 保存不可 になります。 これらは台帳に入力されません。 未保存とはカウントされず、保存は表示されません。それがピボットの正しい宣言です 派生データに対して — 相関エンジンの出力は書き戻すものではありません — そしてそれが何であるか 永久的な「210 未保存」を解決策なしで阻止します。
部分的な書き込み、再試行、ソース システムが作成した ID など、その全体。 元に戻すと、ピボット結果の保存 に達する場合とそうでない場合があります。
ピボットレールモード
1 を使用すると、表面を歩きます。重要なことは設定するときではなく、 それを推進する: モードに入るということは、プロバイダーを呼び出す意図であり、保持することと同様です。 Pivot ▸ コンテキスト メニューで開きます — そのままにすると、進行中のすべての質問が停止します。 選ぶだけなら費用はかかりません。選択は、ドラッグ、スタイル設定、一括操作のジェスチャでもあります これにより、ライブラリは投機的にプロバイダーを呼び出すことがなくなります。
モードはレジストリ ** で **ゲートされています:
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 } // neverしたがって、ピボットを登録しない消費者には、その機能の痕跡が表示されません。それは決してありません viewer または static モードでマウントされます。
原点のないピボット
一部のエンリッチメントには、検索、インポート、リストの貼り付けなど、実行するノードがありません。 インジケーター。 origin: 'none' を宣言すると、ピボットは [] を受け取ります。原点が次の場合に適用されます。 空であり、1 つが選択されると、原点なし グループに折りたたまれます。結果のルートスルー 同じトリアージペイン、同じゲート、同じ来歴モデルで、それらは次の場所に到達します。 ビューポートの中心。
appliesTo は、これらのいずれかについては意味がないため、相談されません。
宣言された可能性
ノードの縁に「ここには 2,100 の相関関係がある」というヒントが、誰かによってそこに置かれました データをロードしました。
node.setPotential('correlations', 2100) // 0 clears it
node.getPotential('correlations')
node.setPotential(2199) // the total across every pivot
node.getPotential()これは 宣言 であり、クエリされることはありません。図書館はバッジを描画するためにプロバイダーを呼び出すことはありません。 なぜなら、質問された瞬間に 1 つのノード上で発生したカウントは、 2 つの異なる物語を語るキャンバス。バッジをクリックすると、その範囲を対象としたピボット モードが開きます ピボット。
Node の他のセッターと同様に、ノードをダーティとしてマークします: graph.renderer.update() を呼び出します。 他に何もレンダリングしようとしていない場合。
縁が描くもの
デフォルトでは、リム バッジはピボット モードが開いている間のみ表示され、リム バッジはピボット モードの選択に役立ちます。 起源;そこをクリックすると、パネルの範囲がそのノードとピボットに設定されます。 pivotRimBadgeVisible 次の場合に変更されます。
new Pivotick(el, data, { pivots, pivotRimBadgeVisible: 'always' })
graph.pivots.rimBadgeVisible = 'pivot-mode' // or later; it dirties every node'pivot-mode' (デフォルト) | ピボットレールモードが開いている間のみ。 |
'always' | どのようなモードであっても、バッジはピボット モードへの手段でもあります。 |
'never' | まったくそうではありません。 |
表示されたら、次は形状を選択します。
ノードには 4 つのリム コーナーがありますが、子を持つと 2 つだけになります - 拡張アフォーダンス 東側を確保します。したがって、プロバイダーごとに 1 つのバッジを読み取ることができるのはほんの一握りですが、読み取りは不可能です。 百。 pivotRimBadge は形状を選択します:
new Pivotick(el, data, { pivots, pivotRimBadge: 'summary' })
graph.pivots.rimBadge = 'per-pivot' // or later; it dirties every node'per-pivot' (デフォルト) | ポテンシャルを宣言したピボットごとに 1 つのバッジ、それぞれがそのピボットを開きます。そのうちのいくつかは、子を持つノードの早い段階で +n に崩壊します。 |
'summary' | プロバイダーの数に関係なく、1 バッジ。ピボット ID なしで宣言された合計、または何も宣言されていない場合に適用されるピボットの数が表示されます。 |
'off' | 何もありません。 NodeStyle.badges はまだ引きます。 |
summary バッジがクリックされたときの動作とそのカウントの由来
summary では、バッジは 1 つのジェスチャです。つまり、そのノードのピボット パネルが開きます。 実際のプロバイダー数のパネルには、カウントと選択肢が表示されます。例外はノードです 正確に 1 つのピボットが適用される * と * では、 no summarize が宣言されます — 最初に読み取るものは何もなく、 絞り込むものは何もありません。クリックするだけで実行されます。結果が到着するのはプロバイダー自身の呼び出しです。 autoIngest は、他の場所と同様に、キャンバスとトリアージ ペインのどちらかを決定します。
適用するピボットの数は宣言されるのではなく導出されるため、プロバイダー呼び出しのコストはかかりません。開催されます グラフの変更と、レジストリまたはグラフのノードの移動時に更新されるまでの間。 appliesTo 他のものを読むとリムに遅れる可能性がありますが、パネルは常に正確な答えです。
キャッシング
ライブラリは 1 つのものをキャッシュします: (ピボット、原点、絞り込み) をキーとした summarize 結果、 なぜなら、複数選択の要約は何も分解できない集合体だからです。すべて それ以外はあなたのものです:
graph.pivots.invalidate() // all of it
graph.pivots.invalidate('correlations') // one pivot's
graph.pivots.invalidate('correlations', nodes) // only what was asked about these認証、再試行、レート制限、および独自のキャッシュはプロバイダー機能に属します。 これはあなたが書いた単純な関数です。
ソースが提供するもの
上記のページは、プロバイダーを作成する人を対象としています。これは、以下から読み取ったものと同じ契約です。 もう一方の側では、それが呼び出すサービスの所有者にとって。
- フェッチ呼び出し、オブジェクトとその関係を返します。唯一必要なもの。 2 それに関する事柄は重要です: 1 つのリクエストで 多くの ID が必要となるため、50 ノード以上のピボットが必要になります コストは 1 回の呼び出しであり、返される ID は 実行間で安定 です。 重複排除キーがオンになっています。応答ごとの ID により、昨日の結果が新しい候補として再び表示されます。
- カウント呼び出し、同じ ID と同じフィルターを取得し、合計と 絞り込める値ごとのカウント。それが門を作るものであり、 作業を絞り込む必要があり、変更のたびに再要求されるため、安くなければなりません。 概算で問題ありません: カウントは推奨です。 1つもなければ ピボットはまだ実行されていますが、Run がそのままで、それを制御するものは何もありません。
- 同じフィルタは両方で尊重されます。 それらを無視するカウントは決して移動しないため、ピボットは その上限を超えるとブロックを解除することはできません。
- キャンセルする方法。 アナリストが先に進むと質問は放棄され、
signal渡されたプロバイダーは、トランスポートがそれに基づいて動作する場合にのみ渡す価値があります。 - 書き込み呼び出し、結果を書き戻すことができる場合。 ID を返す必要があります 割り当てられているため、
canonicalIdsを停止できます 明日は保存されたものを複製して実行し、部分的な成功を報告できるようになります。
どちらの読み取り呼び出しも、失敗とは別に空の応答を通知する必要があります。結果がないことは事実です。 データ、エラーはサービスに関する事実であり、エラーとはまったく異なる解釈をします。 アナリスト。
ここでは、ページネーション、カーソル、ストリーミング トランスポートを必要とするものは何もありません。狭めることは置き換えるものです 彼ら。
これは何ではないのか
- ストリーミングもサーバー カーソルもありません。 2 つをフェッチするのではなく、絞り込みによって置き換えられます。 千行ある場合、そこに何があるのかを尋ね、分析者に選択させます。契約では決して 多くのソースにはストリーミング バックエンドがないためです。
- 再ペアレント化はありません。 返されたノードが既存の親に名前を付けることはありません。 ID による子ユニオン これは、取り込みが既存のノードの子にアクセスする唯一の方法です。
- プロパティ編集を元に戻すことはできません。
graph.historyは、キャンバスに保持されているものと表示されているものをカバーします。 入ってきたもの、出て行ったもの、隠されたもの。ノードのデータは、ライブラリが指定していないバックエンド状態です したがって、フィールドをローカルに戻すのはonBeforeNodeEditCommitの仕事であり、私たちの仕事ではありません。 - トランスポート ポリシーはありません。
saveは書き戻しますが、資格情報、再試行、レート制限、および 競合検出はすべて消費者が行うものです。図書館の唯一の再試行は分析者による再試行です 再試行。 - リロード全体で拒否メモリはありません。 拒否は 1 つのセッションのコンテキストでの判定です。 そして、同じ候補者でも、別のグラフでは異なる回答に値する可能性があります。拒否は、 セッション中は保持され、それ以上は保持されません。
- すでに存在するものは更新されません。 ID が一致した候補は既存のノードから離れます。 手つかずの;新しいコピーに対してノードを調整するには、ライブラリの競合ポリシーが必要です はありません。