Aller au contenu

Options de filtre ​

En mode full, l'en-tête comporte un panneau Graph Filters (l'icône en entonnoir, ou Shift+K) dont le formulaire est construit pour vous. Par défaut, la bibliothèque dérive ce formulaire en analysant les données des nœuds ; UI.filter vous permet de prendre le relais.

ts
const options = {
    UI: {
        filter: {
            facets: [ /* … */ ],   // declare the facets — the form follows
            excludeKeys: ['uuid'], // or just prune the derived ones
        },
    },
}

UI.filter.enabled: false supprime le panneau, sa pilule et Shift+K au total ; graph.queryEngine filtre toujours le code.

La forme s'applique toute seule : une sélection ou une coche s'engage au moment où vous l'effectuez, un champ de texte ou de motif un battement après que vous arrêtez de taper. Il n'y a pas de bouton Appliquer, le panneau ne peut donc jamais afficher un filtre que le canevas n'a pas. Reset, à côté de l'étiquette de section, efface tous les filtres d'attributs à la fois.

Filtrage des bords, pas des nœuds

Tout sur cette page sélectionne nodes. Pour filtrer edges — en masquant tout un type de relation sans déplacer le graphique — déclarez UI.filter.edgeFacets à la place ; ils réutilisent le même vocabulaire de facettes et sont documentés sous Couches de bord.

Dérivation automatique (valeur par défaut) ​

En l'absence de UI.filter, le panneau parcourt les clés de données de chaque nœud, collecte les valeurs distinctes par clé et sélectionne un widget parmi ce qu'il trouve :

À quoi ressemblent les valeursWidget
tous les numérosnumberRange (min/max)
3+ cordes courtesmultiselect
1 à 2 cordes courtesselect
booléensselect (vrai / faux)
autre chosetext

Les étiquettes sont embellies à partir de la clé (attr-type → Attr Type). Il s'agit d'un bon paramètre par défaut sans configuration pour les données exploratoires, mais il n'a aucun moyen de savoir quelles clés sont des facettes et lesquelles sont des internes - un uuid devient une liste déroulante avec une option par nœud, et un long champ de texte libre se dégrade en une zone de texte.

excludeKeys ​

La solution bon marché lorsque la dérivation est presque correcte : supprimez les touches qui font du bruit.

ts
UI: { filter: { excludeKeys: ['uuid', 'imageUrl', 'description'] } }

Facettes déclarées ​

Définissez facets et le formulaire est généré à partir de votre déclaration à la place - exactement les champs que vous répertoriez, dans l'ordre dans lequel vous les répertoriez, avec vos étiquettes. La dérivation automatique est entièrement ignorée, donc rien ne se produit lorsque les données changent.

ts
UI: {
    filter: {
        facets: [
            { key: 'category', label: 'Category', type: 'multiselect',
              options: (graph) => distinct(graph, 'category') },
            { key: 'to_ids',   label: 'IDS flag', type: 'boolean' },
            { key: 'value',    label: 'Value',    type: 'regex' },
        ],
    },
}

Le key d'une facette est également la clé utilisée par queryEngine.setFilter(key, …), donc le filtrage programmatique et le panneau restent sur le même pied.

Types de facettes ​

  • text — sous-chaîne ou correspondance exacte, selon matchMode.
  • regex — un modèle, compilé sans tenir compte de la casse. Un modèle non compilable est signalé sur le terrain et laisse les filtres actifs intacts.
  • select / multiselect — choisissez parmi options. Transmettez un tableau ou une fonction résolue par rapport au graphique en direct afin que les listes d'options suivent les données sans que l'ensemble de facettes lui-même ne change.
  • numberRange — une paire min/max.
  • boolean — une liste déroulante vrai/faux/non défini.

Étiquettes ​

Un label déclaré est utilisé verbatim, il peut donc provenir de votre couche de traduction. Omettez-le et la clé est embellie comme dans l'auto-dérivation.

Facettes calculées : accessor ​

Par défaut, une facette lit node.getData()[key]. Un accessor lit ce que vous voulez, y compris les éléments qui ne sont pas du tout sur le nœud :

ts
// "this object contains an attribute of type X" — reads the node's children
{ key: 'child_type', label: 'Contains attribute of type', type: 'multiselect',
  options: () => ATTRIBUTE_TYPES,
  accessor: (node) => node.children.map((child) => child.getData()['attr-type']) }

Renvoyer un tableau est très bien – voir tableaux correspondants ci-dessous.

Contrôle total : predicate ​

Un predicate décide lui-même de l'adhésion et l'emporte sur accessor / matchMode. Il s'exécute par nœud et par application de filtrage, alors restez bon marché.

ts
{ key: 'busy', label: 'Busy services', type: 'text',
  predicate: (node, value) => node.getData().load >= Number(value) }

Une facette dont le lancer accessor ou predicate cesse de correspondre (et avertit une fois) plutôt que de supprimer le rendu avec elle.

Commande ​

Les champs suivent l'ordre de déclaration. Définissez order sur une facette pour la placer explicitement — pratique lorsque les facettes sont assemblées à plusieurs endroits.

Correspondance ​

matchMode décide de la manière dont une valeur de filtre est comparée :

  • 'exact' par défaut — égalité stricte. Pour une sélection multiple (un tableau de choix), il s'agit de l'adhésion : n'importe lequel.
  • 'partial' — correspondance de sous-chaîne, String(nodeValue).includes(value).
  • 'all' — et-sémantique : chaque valeur sélectionnée doit être présente.

Tableaux correspondants ​

Lorsque la valeur d'un nœud est un array — data.tags = ['tlp:amber', 'malware'] — un filtre teste appartenance, pas l'égalité :

ts
// matches nodes tagged 'malware'; does NOT match a node tagged 'not-malware'
graph.queryEngine.setFilter('tags', { value: 'malware' })

// any-of: amber OR green
graph.queryEngine.setFilter('tags', { value: ['tlp:amber', 'tlp:green'] })

// all: amber AND green
graph.queryEngine.setFilter('tags', { value: ['tlp:amber', 'tlp:green'], matchMode: 'all' })

Avec 'partial', n'importe quel élément peut correspondre à une sous-chaîne. Un numberRange contre un tableau correspond lorsqu'un élément se situe dans la plage.

Changement de comportement

Avant que cela ne soit pris en charge, la valeur d'un nœud de tableau tombait à String(nodeValue).includes(value) — donc un filtre pour malware correspondait accidentellement à un nœud étiqueté not-malware, et une valeur de filtre de tableau ne correspondait jamais du tout. Les deux se comportent désormais comme décrit ci-dessus.

Négation ​

negate: true conserve ce que le filtre cacherait autrement. Chaque champ du panneau a un commutateur is / is not sur sa ligne d'étiquette qui le définit ; un champ commuté sur is not est dessiné en rouge, ses pics sont barrés.

ts
// every type but domain, including types added later
graph.queryEngine.setFilter('type', { value: ['domain'], negate: true })

// nothing tagged malware
graph.queryEngine.setFilter('tags', { value: ['malware'], negate: true })

Il se lit dans l'autre sens pour chaque widget : une liste est aucun de, un champ de texte ne contient pas (avec 'partial'), un motif ne correspond pas à, une plage à l'extérieur de. Avec 'all', c'est pas tout le. La réponse d'une facette predicate est inversée.

Un nœud avec aucune valeur pour la clé est conservé, car ce n'est pas non plus un domaine. Ainsi, un filtre et sa négation divisent toujours le graphe en deux, chaque nœud étant exactement d'un côté. Une valeur qui ne restreint rien (une liste vide, une chaîne vide, une plage ouverte, un modèle inutilisable) ne restreint toujours rien.

Les champs vrai/faux n'ont pas de commutateur : n'est pas vrai est est faux. Les filtres de bord (setEdgeFilter) prennent également negate. Une section legend qui partage la clé d'une facette maintient un filtre annulé lorsque vous cliquez dessus.

Masquer les nœuds non connectés ​

Le filtrage des relations laisse les nœuds derrière. Désactivez un edge layer et un nœud dont la seule relation y était reste sur le canevas sans rien attaché - ce qui est délibéré, car un calque est une lentille. Lorsque vous do voulez que ces nœuds disparaissent, il s'agit d'une règle côté nœud :

ts
const options = {
    UI: {
        filter: { hideDisconnected: true },
    },
}

Un nœud est masqué lorsqu'il ne lui reste plus de bord visible, compté après que les couches et les filtres de nœuds ont eu leur mot à dire. Une auto-boucle compte comme une relation ; une note épinglée à un nœud ne le fait pas.

Le même commutateur se trouve dans le menu déroulant View sous la forme Hide non connecté, sur chaque graphique, désactivé à moins que l'option ne l'active — afin qu'un utilisateur puisse nettoyer les orphelins d'un calque basculé vers la gauche ou remettre ceux que vous avez masqués. Pendant qu'il se cache, la ligne indique combien.

Celui-ci déplace le graphique

Tout le reste sur cette page, et chaque couche de bord, laisse la mise en page seule. Un nœud caché quitte la simulation, donc le reste se réinstalle. C’est l’échange contre une toile bien rangée, et c’est pourquoi la règle est désactivée par défaut.

Deux choses qu’il ne fait délibérément pas :

  • Les intérieurs des clusters sont laissés seuls. Les clusters regroupent régulièrement des nœuds sans relation entre eux, donc appliquer la règle à l'intérieur d'un cluster ouvrirait une boîte vide.
  • Rien n'est protégé. Désactivez chaque couche et rien n'est connecté, donc rien n'est dessiné. Le changement est le chemin du retour.
ts
graph.queryEngine.setHideDisconnected(true)  // same as the switch
graph.queryEngine.isHideDisconnected()
graph.queryEngine.getDisconnectedNodeCount() // what the switch row reports

Réappliquer après les modifications des données ​

Les filtres sont appliqués lorsqu'un filtre change, et non lorsque les données du graphique changent. Ajoutez le bord manquant à un nœud et il reste caché jusqu'à ce que quelque chose soit recalculé :

ts
graph.updateData(newNodes, newEdges)
graph.queryEngine.reapply()   // recompute now

A savoir avant de l'appeler : la re-dérivation de la visibilité ** annule un manuel graph.hideNode()**, dont rien ne se souvient. queryEngine.excludeNode() est le cache qui survit — c'est ce que le menu contextuel et les actions groupées utilisent, et seul Afficher le nœud l'inverse.

Où le panel tire ses valeurs ​

Le filtre valeurs passe toujours par graph.queryEngine, quelle que soit la manière dont le formulaire est construit — de sorte que le nombre de filtres actifs de l'en-tête, la liste Nœuds cachés et la propagation dans le sous-graphe d'un cluster étendu continuent de fonctionner. Les facettes déclarées sont également transmises au sous-graphe d'un cluster, donc une facette accessor / predicate filtre les enfants exactement comme elle filtre les nœuds de niveau supérieur.

Le canevas legend est une troisième voie d'accès au même moteur : cliquer sur une ligne de légende masque cette catégorie. Donnez à la légende le key d'une facette select / multiselect déclarée et les deux lecteurs un filtre, afin que le contrôle du panneau et la légende restent en phase.

La table de données est une quatrième : son bouton Appliquer au graphique masque les éléments omis par ses filtres de colonnes. Cette poussée atterrit sous une clé réservée qui lui est propre, elle survit donc à tout ce que le formulaire de ce panneau s'applique et est combinée avec tout ce que ce panneau filtre plutôt que de le remplacer. Étant donné que le formulaire ne peut structurellement pas afficher une clé réservée, le panneau développe une ligne à partir du tableau nommant ce que le push cache - et le seul autre endroit d'où il peut être effacé, une fois le dock replié.

Voir la carte de galerie Filtre / moteur de requête pour un exemple en direct, et FilterFacet pour le type complet.