Κοινή χρήση μέσω


Το 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();
}

Ζητήματα προς εξέταση και περιορισμοί

  • Αυτό το φίλτρο υποστηρίζεται μόνο για αντιστοίχιση δεδομένων μήτραςΠροβολή.

  • Η απεικόνιση θα πρέπει να περιέχει μόνο έναν ρόλο ομαδοποίησης δεδομένων.

  • Μια απεικόνιση που χρησιμοποιεί τον τύπο φίλτρου ταυτότητας Ιεραρχία θα πρέπει να εφαρμόζει μόνο ένα μοναδικό φίλτρο αυτού του τύπου.