Zdieľať cez


Rozhranie API na filtrovanie hierarchickej identity vo vizuáloch služby Power BI

Rozhranie API filtra identity hierarchie umožňuje vizuálom, ktoré používajú priradenie Matrix DataView na filtrovanie údajov podľa viacerých polí súčasne na základe údajových bodov, ktoré používajú štruktúru hierarchie.

Toto rozhranie API je užitočné v nasledujúcich scenároch:

  • Filtrovanie hierarchií na základe údajových bodov
  • Vlastné vizuály, ktoré používajú sémantické modely so skupinou v kľúčoch

Poznámka

Rozhranie API filtra identity hierarchie je k dispozícii od rozhrania API verzie 5.9.0

Rozhranie filtra je zobrazené v nasledujúcom kóde:

interface IHierarchyIdentityFilter<IdentityType> extends IFilter {
    target: IHierarchyIdentityFilterTarget;
    hierarchyData: IHierarchyIdentityFilterNode<IdentityType>[];
}
  • $schema: https://powerbi.com/product/schema#hierarchyIdentity (zdedené z IFilter)

  • filterType: FilterType.HierarchyIdentity (zdedené z IFilter)

  • target: Pole relevantných stĺpcov v dotaze. V súčasnosti je podporovaná len jedna rola. Cieľ sa preto nevyžaduje a mal by byť prázdny.

  • hierarchyData : vybraté a nevybrané položky v strome hierarchie, kde každý z nich IHierarchyIdentityFilterNode<IdentityType> predstavuje jeden výber hodnoty.

type IHierarchyIdentityFilterTarget = IQueryNameTarget[]

interface IQueryNameTarget {
    queryName: string;
}
  • queryName: názov dotazu zdrojového stĺpca v dotaze. Pochádza z DataViewMetadataColumn
interface IHierarchyIdentityFilterNode<IdentityType> {
    identity: IdentityType;
    children?: IHierarchyIdentityFilterNode<IdentityType>[];
    operator: HierarchyFilterNodeOperators;
}
  • identity: Identita Node v zobrazení DataView. Hodnota IdentityType by mala byť CustomVisualOpaqueIdentity

  • children: Zoznam detí uzla relevantných pre aktuálny výber

  • operator: Operátor pre jednotlivé objekty v strome. Operátor môže byť jednou z nasledujúcich troch možností:

    type HierarchyFilterNodeOperators = "Selected" | "NotSelected" | "Inherited";
    
    • Vybratá: hodnota je explicitne vybratá.

    • NotSelected: hodnota nie je výslovne vybratá.

    • Zdedené: výber hodnoty je podľa nadradenej hodnoty v hierarchii alebo predvolená, ak ide o koreňovú hodnotu.

Pri definovaní filtra identity hierarchie majte na pamäti nasledujúce pravidlá:

  • Vezmite identity z zobrazenia Údajov.
  • Každá cesta identity by mala byť platnou cestou v zobrazení údajov.
  • Každý list by mal mať operátor Selected (Vybraté) alebo NotSelected (Nevybrané).
  • Ak chcete porovnať identity, použite ICustomVisualsOpaqueUtils.compareCustomVisualOpaqueIdentities funkciu .
  • Identity sa môžu zmeniť v nasledujúcich zmenách polí (napríklad pridaním alebo odstránením polí). Služba Power BI priradí aktualizované identity k existujúcemu filtru filter.hierarchyData.

Používanie rozhrania API filtra identity hierarchie

Nasledujúci kód je príkladom toho, ako používať rozhranie API filtra identity hierarchie vo vlastnom vizuáli:

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();

Ak chcete použiť filter, použite applyJsonFilter volanie rozhrania API:

this.host.applyJsonFilter(filter, "general", "filter", action);

Ak chcete obnoviť aktívny filter JSON, použite jsonFilters vlastnosť v časti VisualUpdateOptions:

export interface VisualUpdateOptions extends extensibility.VisualUpdateOptions {
   //...
   jsonFilters?: IFilter[];
}

Filter HierarchyIdnetity je podporovaný len pre hierarchicky usporiadané polia. Služba Power BI predvolene neoveruje, či sú polia hierarchicky usporiadané.

Ak chcete aktivovať hierarchicky súvisiace overenie, pridajte vlastnosť "areHierarchicallyRelated" do príslušnej podmienky roly v súbore capabilities.json:

"dataViewMappings": [
    {
         "conditions": [
             {
                  "Rows": {
                      "min": 1,
                      "areHierarchicallyRelated": true <------ NEW ------>
                  },
                  "Value": {
                  "min": 0
                  }
            }
        ],
        ...
    }
]

Ak sú splnené tieto podmienky, polia sú hierarchicky usporiadané:

  • Medzi zahrnuté možnosti vzťahu nie je ani kardinalita typu many-to-many, ani ConceptualNavigationBehavior.Weak.

  • Všetky polia vo filtri existujú v ceste.

  • Každý vzťah na ceste má rovnaký smer alebo obojsmerne.

  • Smer vzťahu zodpovedá kardinalite jedného k mnohým alebo obojsmerne.

Príklad vzťahov hierarchie

Napríklad vzhľadom na nasledujúci vzťah entity:

Diagram znázorňujúci obojsmernú povahu filtra.

  • A, B sú hierarchicky usporiadané: true
  • B, C sú hierarchicky usporiadané: true
  • A, B, C sú hierarchicky usporiadané: true
  • A, C, E sú hierarchicky usporiadané: true (A --> E --> C)
  • A, B, E sú hierarchicky usporiadané: true (B --> A --> E)
  • A, B, C, E sú hierarchicky usporiadané: true (B --> A --> E --> C)
  • A, B, C, D sú hierarchicky usporiadané: false (porušilo pravidlo č. 3)
  • C, D sú hierarchicky usporiadané: true
  • B, C, D sú hierarchicky usporiadané: false (porušilo pravidlo č. 3)
  • A, C, D, E sú hierarchicky usporiadané: false (porušilo pravidlo č. 3)

Poznámka

  • Keď sú tieto overenia povolené a polia nesúvisia hierarchicky, vizuál sa nevykreslí a zobrazí sa chybové hlásenie:

    Snímka obrazovky znázorňujúca vizuál s povolenými overeniami, ktoré sa nedajú načítať, pretože polia nie sú hierarchicky prepojené. V chybovom hlásení sa uvádza, že používate polia, ktoré nemajú podporovaný súbor vzťahov.

    Snímka obrazovky znázorňujúca chybové hlásenie, keď sú overenia povolené a polia nie sú hierarchicky usporiadané. V správe sa zobrazuje hlásenie

  • Keď sú tieto overenia zakázané a vizuál filtra použije filter obsahujúci uzly súvisiace s ne hierarchicky usporiadaným poliami, ostatné vizuály sa pri používaní mierok nemusia správne vykresliť:

    Snímka obrazovky znázorňujúca vizuál so zakázanými overeniami, ktoré sa nedajú načítať, pretože polia nie sú hierarchicky usporiadané. Chybové hlásenie s informáciou:

    Snímka obrazovky znázorňujúca chybové hlásenie, keď sú overenia zakázané a polia nie sú hierarchicky usporiadané. Zobrazí sa hlásenie

Príklad kódu na aktualizáciu stromu údajov hierarchie po novom výbere

Nasledujúci kód ukazuje, ako aktualizovať hierarchyData strom po novom výbere:

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

Dôležité informácie a obmedzenia

  • Tento filter je podporovaný len pre priradenie matice dataView.

  • Vizuál by mal obsahovať iba jednu rolu zoskupeniaúdajov.

  • Vizuál, ktorý používa typ filtra identity hierarchie, by mal použiť iba jeden filter tohto typu.