Το API φίλτρων ιεραρχικής ταυτότητας στις απεικονίσεις Power BI
Το API φίλτρου ταυτότητας ιεραρχίας επιτρέπει στις απεικονίσεις που χρησιμοποιούν αντιστοίχιση δεδομένων μήτρας να φιλτράρουν δεδομένα σε πολλά πεδία ταυτόχρονα με βάση τα σημεία δεδομένων που χρησιμοποιούν μια δομή ιεραρχίας.
Αυτό το API είναι χρήσιμο στα παρακάτω σενάρια:
- Φιλτράρισμα ιεραρχιών με βάση τα σημεία δεδομένων
- Προσαρμοσμένες απεικονίσεις που χρησιμοποιούν σημασιολογικά μοντέλα με ομαδοποίηση σε κλειδιά
Σημείωμα
Το API φίλτρου ταυτότητας ιεραρχίας είναι διαθέσιμο από την έκδοση API 5.9.0
Η διασύνδεση φίλτρου εμφανίζεται στον παρακάτω κώδικα:
interface IHierarchyIdentityFilter<IdentityType> extends IFilter {
target: IHierarchyIdentityFilterTarget;
hierarchyData: IHierarchyIdentityFilterNode<IdentityType>[];
}
$schema:
https://powerbi.com/product/schema#hierarchyIdentity
(μεταβιβάστηκε από το IFilter)filterType: FilterType.HierarchyIdentity (μεταβιβαστεί από το IFilter)
target: Πίνακας σχετικών στηλών στο ερώτημα. Προς το παρόν, υποστηρίζεται μόνο ένας ρόλος. Επομένως, ο προορισμός δεν απαιτείται και θα πρέπει να είναι κενός.
hierarchyData: τα επιλεγμένα και μη επιλεγμένα στοιχεία σε ένα δέντρο ιεραρχίας όπου το καθένα
IHierarchyIdentityFilterNode<IdentityType>
αντιπροσωπεύει μια επιλογή μοναδικής τιμής.
type IHierarchyIdentityFilterTarget = IQueryNameTarget[]
interface IQueryNameTarget {
queryName: string;
}
- queryName: το όνομα ερωτήματος της στήλης προέλευσης στο ερώτημα. Προέρχεται από το
DataViewMetadataColumn
interface IHierarchyIdentityFilterNode<IdentityType> {
identity: IdentityType;
children?: IHierarchyIdentityFilterNode<IdentityType>[];
operator: HierarchyFilterNodeOperators;
}
identity: Η ταυτότητα Node στην DataView. Το
IdentityType
θα πρέπει ναCustomVisualOpaqueIdentity
children: Λίστα θυγατρικών κόμβων που σχετίζονται με την τρέχουσα επιλογή
operator: Ο τελεστής για μεμονωμένα αντικείμενα στο δέντρο. Ο τελεστής μπορεί να είναι μία από τις παρακάτω τρεις επιλογές:
type HierarchyFilterNodeOperators = "Selected" | "NotSelected" | "Inherited";
Επιλεγμένη: η τιμή επιλέγεται ρητά.
NotSelected: Η τιμή δεν επιλέγεται ρητά.
Μεταβίβαση: Η επιλογή τιμής είναι σύμφωνα με τη γονική τιμή στην ιεραρχία ή η προεπιλογή εάν είναι η τιμή ρίζας.
Λάβετε υπόψη τους ακόλουθους κανόνες κατά τον ορισμό του φίλτρου ταυτότητας ιεραρχίας:
- Πάρε τις ταυτότητες από την DataView.
- Κάθε διαδρομή ταυτότητας πρέπει να είναι μια έγκυρη διαδρομή στην DataView.
- Κάθε φύλλο πρέπει να έχει έναν τελεστή Selected ή NotSelected.
- Για να συγκρίνετε ταυτότητες, χρησιμοποιήστε τη
ICustomVisualsOpaqueUtils.compareCustomVisualOpaqueIdentities
συνάρτηση. - Οι ταυτότητες ενδέχεται να αλλάξουν τις ακόλουθες αλλαγές στα πεδία (για παράδειγμα, προσθήκη ή κατάργηση πεδίων). Το Power BI αντιστοιχίζει τις ενημερωμένες ταυτότητες στο υπάρχον filter.hierarchyData.
Τρόπος χρήσης του API φίλτρου ταυτότητας ιεραρχίας
Ο ακόλουθος κώδικας είναι ένα παράδειγμα του τρόπου χρήσης του API φίλτρου ταυτότητας ιεραρχίας σε μια προσαρμοσμένη απεικόνιση:
import { IHierarchyIdentityFilterTarget, IHierarchyIdentityFilterNode, HierarchyIdentityFilter } from "powerbi-models"
const target: IHierarchyIdentityFilterTarget = [];
const hierarchyData: IHierarchyIdentityFilterNode<CustomVisualOpaqueIdentity>[] = [
{
identity: {...},
operator: "Selected",
children: [
{
identity: {...},
operator: "NotSelected"
}
]
},
{
identity: {...},
operator: "Inherited",
children: [
{
identity: {...},
operator: "Selected"
}
]
}
];
const filter = new HierarchyIdentityFilter(target, hierarchyData).toJSON();
Για να εφαρμόσετε το φίλτρο, χρησιμοποιήστε την applyJsonFilter
κλήση API:
this.host.applyJsonFilter(filter, "general", "filter", action);
Για να επαναφέρετε το ενεργό φίλτρο JSON, χρησιμοποιήστε την jsonFilters
ιδιότητα που βρίσκεται στην "VisualUpdateOptions":
export interface VisualUpdateOptions extends extensibility.VisualUpdateOptions {
//...
jsonFilters?: IFilter[];
}
Επικύρωση πεδίων που σχετίζονται με ιεραρχία (προαιρετικό)
Το HierarchyIdnetity
φίλτρο υποστηρίζεται μόνο για πεδία που σχετίζονται ιεραρχικά. Από προεπιλογή, το Power BI δεν επαληθεύει εάν τα πεδία σχετίζονται ιεραρχικά.
Για να ενεργοποιήσετε την επικύρωση που σχετίζεται ιεραρχικά, προσθέστε την ιδιότητα "areHierarchicallyRelated" στη σχετική συνθήκη ρόλου στο αρχείο capabilities.json:
"dataViewMappings": [
{
"conditions": [
{
"Rows": {
"min": 1,
"areHierarchicallyRelated": true <------ NEW ------>
},
"Value": {
"min": 0
}
}
],
...
}
]
Τα πεδία σχετίζονται ιεραρχικά εάν πληρούνται οι ακόλουθες συνθήκες:
Κανένα συμπεριλαμβανόμενο άκρο σχέσης δεν είναι πληθικότητα πολλά προς πολλά, ούτε
ConceptualNavigationBehavior.Weak
.Όλα τα πεδία στο φίλτρο υπάρχουν στη διαδρομή.
Κάθε σχέση στη διαδρομή έχει την ίδια κατεύθυνση ή αμφίδρομη.
Η κατεύθυνση της σχέσης συμφωνεί με την πληθικότητα για ένα προς πολλά ή αμφίδρομη.
Παράδειγμα σχέσεων ιεραρχίας
Για παράδειγμα, δεδομένης της παρακάτω σχέσης οντότητας:
- Τα A, B σχετίζονται ιεραρχικά: true
- B, C σχετίζονται ιεραρχικά: true
- Οι A, B, C σχετίζονται ιεραρχικά: true
- Τα A, C, E σχετίζονται ιεραρχικά: true (A --> E --> C)
- Τα A, B, E σχετίζονται ιεραρχικά: true (B --> A --> E)
- Τα A, B, C, E σχετίζονται ιεραρχικά: true (B --> A --> E --> C)
- Οι A, B, C, D σχετίζονται ιεραρχικά: false (παραβιασμένη κανόνας #3)
- C, D σχετίζονται ιεραρχικά: true
- B, C, D σχετίζονται ιεραρχικά: false (παραβιασμένη κανόνας #3)
- Οι A, C, D, E σχετίζονται ιεραρχικά: false (παραβιασμένη κανόνας #3)
Σημείωμα
Όταν ενεργοποιηθούν αυτές οι επικυρώσεις και τα πεδία δεν σχετίζονται ιεραρχικά, η απεικόνιση δεν θα αποδοθεί και θα εμφανιστεί ένα μήνυμα σφάλματος:
Όταν αυτές οι επικυρώσεις είναι απενεργοποιημένες και η απεικόνιση φίλτρου εφαρμόζει ένα φίλτρο που περιέχει κόμβους που σχετίζονται με πεδία που δεν σχετίζονται ιεραρχικά, άλλες απεικονίσεις ενδέχεται να μην αποδίδονται σωστά όταν χρησιμοποιούνται μετρήσεις:
Παράδειγμα κώδικα για την ενημέρωση του δέντρου δεδομένων ιεραρχίας μετά τη νέα επιλογή
Ο παρακάτω κώδικας δείχνει πώς μπορείτε να ενημερώσετε το hierarchyData
δέντρο μετά από μια νέα επιλογή:
type CompareIdentitiesFunc = (id1: CustomVisualOpaqueIdentity, id2: CustomVisualOpaqueIdentity) => boolean;
/**
* Updates the filter tree following a new node selection.
* Prunes irrelevant branches after node insertion/removal if necessary.
* @param path Identities path to the selected node.
* @param treeNodes Array of IHierarchyIdentityFilterNode representing a valid filter tree.
* @param compareIdentities Compare function for CustomVisualOpaqueIdentity to determine equality. Pass the ICustomVisualsOpaqueUtils.compareCustomVisualOpaqueIdentities function.
* @returns A valid filter tree after the update
*/
function updateFilterTreeOnNodeSelection(
path: CustomVisualOpaqueIdentity[],
treeNodes: IHierarchyIdentityFilterNode<CustomVisualOpaqueIdentity>[],
compareIdentities: CompareIdentitiesFunc
): IHierarchyIdentityFilterNode<CustomVisualOpaqueIdentity>[] {
if (!path) return treeNodes;
const root: IHierarchyIdentityFilterNode<CustomVisualOpaqueIdentity> = {
identity: null,
children: treeNodes || [],
operator: 'Inherited',
};
let currentNodesLevel = root.children;
let isClosestSelectedParentSelected = root.operator === 'Selected';
let parents: { node: IHierarchyIdentityFilterNode<CustomVisualOpaqueIdentity>, index: number }[] = [{ node: root, index: -1 }];
let shouldFixTree = false;
path.forEach((identity, level) => {
const index = currentNodesLevel.findIndex((node) => compareIdentities(node.identity, identity));
const isLastNodeInPath = level === path.length - 1
if (index === -1) {
const newNode: IHierarchyIdentityFilterNode<CustomVisualOpaqueIdentity> = {
identity,
children: [],
operator: isLastNodeInPath ? (isClosestSelectedParentSelected ? 'NotSelected' : 'Selected') : 'Inherited',
};
currentNodesLevel.push(newNode);
currentNodesLevel = newNode.children;
if (newNode.operator !== 'Inherited') {
isClosestSelectedParentSelected = newNode.operator === 'Selected';
}
} else {
const currentNode = currentNodesLevel[index];
if (isLastNodeInPath) {
const partial = currentNode.children && currentNode.children.length;
if (partial) {
/**
* The selected node has subtree.
* Therefore, selecting this node should lead to one of the following scenarios:
* 1. The node should have Selected operator and its subtree should be pruned.
* 2. The node and its subtree should be pruned form the tree and the tree should be fixed.
*/
// The subtree should be always pruned.
currentNode.children = [];
if (currentNode.operator === 'NotSelected' || (currentNode.operator === 'Inherited' && isClosestSelectedParentSelected )) {
/**
* 1. The selected node has NotSelected operator.
* 2. The selected node has Inherited operator, and its parent has Slected operator.
* In both cases the node should be pruned from the tree and the tree shoud be fixed.
*/
currentNode.operator = 'Inherited'; // to ensure it will be pruned
parents.push({ node: currentNode, index });
shouldFixTree = true;
} else {
/**
* 1. The selected node has Selected operator.
* 2. The selected node has Inherited operator, but its parent doesn't have Selected operator.
* In both cases the node should stay with Selected operator pruned from the tree and the tree should be fixed.
* Note that, node with Selected oprator and parent with Selector operator is not valid state.
*/
currentNode.operator = 'Selected';
}
} else {
// Leaf node. The node should be pruned from the tree and the tree should be fixed.
currentNode.operator = 'Inherited'; // to ensure it will be pruned
parents.push({ node: currentNode, index });
shouldFixTree = true;
}
} else {
// If it's not the last noded in path we just continue traversing the tree
currentNode.children = currentNode.children || [];
currentNodesLevel = currentNode.children
if (currentNode.operator !== 'Inherited') {
isClosestSelectedParentSelected = currentNode.operator === 'Selected';
// We only care about the closet parent with Selected/NotSelected operator and its children
parents = [];
}
parents.push({ node: currentNode, index });
}
}
});
// Prune brnaches with Inherited leaf
if (shouldFixTree) {
for (let i = parents.length - 1; i >= 1; i--) {
// Normalize to empty array
parents[i].node.children = parents[i].node.children || [];
if (!parents[i].node.children.length && (parents[i].node.operator === 'Inherited')) {
// Remove the node from its parent children array
removeElement(parents[i - 1].node.children, parents[i].index);
} else {
// Node has children or Selected/NotSelected operator
break;
}
}
}
return root.children;
}
/**
* Removes an element from the array without preserving order.
* @param arr - The array from which to remove the element.
* @param index - The index of the element to be removed.
*/
function removeElement(arr: any[], index: number): void {
if (!arr || !arr.length || index < 0 || index >= arr.length) return;
arr[index] = arr[arr.length - 1];
arr.pop();
}
Ζητήματα προς εξέταση και περιορισμοί
Αυτό το φίλτρο υποστηρίζεται μόνο για αντιστοίχιση δεδομένων μήτραςΠροβολή.
Η απεικόνιση θα πρέπει να περιέχει μόνο έναν ρόλο ομαδοποίησης δεδομένων.
Μια απεικόνιση που χρησιμοποιεί τον τύπο φίλτρου ταυτότητας Ιεραρχία θα πρέπει να εφαρμόζει μόνο ένα μοναδικό φίλτρο αυτού του τύπου.