跳转到内容
Pivotick
Main Navigation 首页
快速入门
配置
API 参考
示例库项目在线体验

简体中文

English
Français
Deutsch
Português

简体中文

English
Français
Deutsch
Português

外观

Sidebar Navigation

快速入门

使用 Pivotick 的项目

配置

回调

边图层

简化

布局

渲染

模拟

自定义界面

侧栏

提示框

上下文菜单

筛选

图例

数据表格

界面样式

翻译界面

插件

Pivotick API

拓展与数据增强

保存拓展结果

撤销与历史记录

安全

API 文档

示例库

基础

节点样式

边样式

布局

事件与回调

界面自定义

编辑与创建

备注与注释

筛选、搜索与层级

编程控制

主题与性能

拓展与数据增强

综合示例

过滤/查询引擎 ​

真实的数据集太大,无法一次显示全部。 graph.queryEngine 按节点属性过滤图表:setFilter(key, spec) 将 data[key] 匹配的节点(精确值、列表或数字 { min, max } 范围)和多个过滤器 AND 保留在一起。 resetFilters()清除它们。

抛开属性规则不谈,excludeNode(id)手动隐藏一个节点; includeNode(id) 将其恢复,clearNodeExclusions() 恢复每个手动隐藏的节点。

下面的按钮直接驱动发动机。无需代码即可实现相同的过滤:在完整模式中,标题带有图形过滤器面板(漏斗图标,或按Shift+K)。代码和面板保持同步 - 通过按钮进行过滤,打开面板,其控件已经反映了活动过滤器,而隐藏节点出现在其隐藏节点列表中。

该卡声明了其方面(UI.filter.facets),因此该面板恰好包含下面的六个控件以及给定的标签和小部件 - 包括两个根本不是节点数据键的控件。省略 facets ,面板会为每个数据键派生一个控件,从它找到的值推断每个小部件;请参阅过滤器选项。

tags 是数组值,因此过滤它会测试成员资格:tag = critical 使每个节点都带有该标签。 Depends on load ≥ 是计算的 — accessor 读取依赖项' 负载,而不是节点自己的负载,这就是为什么 web-1(最繁忙的服务,82%)从其中退出,而更安静的 db-1 幸存下来。

将它们堆叠起来: type = api 然后 load ≥ 70 只剩下一个繁忙的 API。

js
// Distinct values of a key across the graph, flattening array-valued data — so a
// select's options follow the data without the facet set itself churning.
function distinct(graph, key) {
    const values = new Set()
    for (const node of graph.getNodes()) {
        const value = node.getData()[key]
        if (Array.isArray(value)) value.forEach((entry) => values.add(entry))
        else if (value !== null && value !== undefined) values.add(String(value))
    }
    return [...values].sort().map((value) => ({ label: value, value }))
}

// Declaring facets replaces the panel's data-scanning: exactly these fields, in
// this order, with these labels and widgets. Omit `UI.filter.facets` entirely and
// the panel derives a control per data key instead (the zero-config default).
const facets = [
    { key: 'type', label: 'Service type', type: 'multiselect', options: (g) => distinct(g, 'type') },
    { key: 'zone', label: 'Region', type: 'select', options: (g) => distinct(g, 'zone') },
    { key: 'load', label: 'Load (%)', type: 'numberRange' },

    // Array-valued data: a filter tests membership, so picking `critical` matches
    // every node carrying that tag — one control, no flattened shadow data key.
    { key: 'tags', label: 'Tag', type: 'multiselect', options: (g) => distinct(g, 'tags') },

    // Computed: `accessor` reads whatever you like, here the *dependencies'* load
    // rather than the node's own data — "which services depend on a busy one?".
    {
        key: 'dep_load', label: 'Depends on load ≥', type: 'numberRange',
        accessor: (node) => node.getEdgesOut().map((edge) => edge.to.getData().load)
    },

    // Full control: decide membership yourself.
    {
        key: 'name', label: 'Name matches', type: 'regex',
        predicate: (node, value) => new RegExp(value, 'i').test(node.id)
    }
]
js
// Everything the built-in panel does is available programmatically on
// graph.queryEngine. setFilter(key, spec) keys off the facet's key and applies at
// once; call it for several keys and the filters AND together.
function filterByType(graph, type) {
    graph.queryEngine.setFilter('type', { value: type, matchMode: 'exact' })
}

// A numeric spec takes a { min, max } range (either bound may be omitted).
function filterByLoad(graph, min) {
    graph.queryEngine.setFilter('load', { value: { min, max: undefined } })
}

// Array-valued data matches by membership: this keeps every node tagged 'critical'.
// Pass an array for any-of, or add matchMode: 'all' to require every tag.
function filterByTag(graph, tag) {
    graph.queryEngine.setFilter('tags', { value: tag })
}

// The computed facet: the node's own load is irrelevant, its dependencies' isn't.
function filterByDependencyLoad(graph, min) {
    graph.queryEngine.setFilter('dep_load', { value: { min, max: undefined } })
}

// resetFilters clears every active filter and restores the full graph.
function clearFilters(graph) {
    graph.queryEngine.resetFilters()
}

// Attribute rules aside, excludeNode(id) hides a single node by hand. It's tracked
// separately from setFilter rules and listed in the panel's "Hidden nodes" section;
// includeNode(id) restores one, clearNodeExclusions() restores them all.
function excludeNode(graph, id) {
    graph.queryEngine.excludeNode(id)
}

function clearExclusions(graph) {
    graph.queryEngine.clearNodeExclusions()
}
js
const options = {
    // Full mode ships the built-in Graph Filters panel (open it from the header's
    // funnel, or Shift+K). Its form is generated from the facets declared below;
    // without `filter.facets` it auto-discovers your node attributes instead.
    UI: {
        mode: 'full',
        // Off: `full` mode brings a minimap and the data dock, and this card is about
        // neither (see the Minimap and Data table cards for those).
        minimap: false,
        table: false,
        filter: { facets }
    },
    render: {
        nodeTypeAccessor: (node) => node.getData().type,
        nodeStyleMap: {
            web: { color: palette.web },
            api: { color: palette.api },
            db: { color: palette.db }
        }
    }
}
js
// A small service-dependency graph. Each node carries categorical fields (type,
// zone), a numeric one (load) and an array-valued one (tags) — exactly the kind of
// data the filter engine and its built-in UI slice on. Colouring by type is purely
// so the filtering reads clearly (see the Node styling cards for the technique).
const palette = { web: '#0072B2', api: '#E69F00', db: '#009E73' }

const data = {
    nodes: [
        { id: 'web-1', data: { type: 'web', zone: 'eu', load: 82, tags: ['public', 'critical'] } },
        { id: 'web-2', data: { type: 'web', zone: 'us', load: 45, tags: ['public'] } },
        { id: 'api-1', data: { type: 'api', zone: 'eu', load: 68, tags: ['internal'] } },
        { id: 'api-2', data: { type: 'api', zone: 'us', load: 91, tags: ['internal', 'critical'] } },
        { id: 'api-3', data: { type: 'api', zone: 'eu', load: 30, tags: [] } },
        { id: 'db-1', data: { type: 'db', zone: 'eu', load: 55, tags: ['critical'] } },
        { id: 'db-2', data: { type: 'db', zone: 'us', load: 74, tags: ['critical'] } }
    ],
    edges: [
        { from: 'web-1', to: 'api-1' },
        { from: 'web-1', to: 'api-3' },
        { from: 'web-2', to: 'api-2' },
        { from: 'api-1', to: 'db-1' },
        { from: 'api-2', to: 'db-2' },
        { from: 'api-3', to: 'db-1' },
        { from: 'db-1', to: 'db-2' }
    ]
}
Pager
下一页快速入门