Læs på engelsk

Del via


API'en til hierarkiske identitetsfiltre i Power BI-visualiseringer

Api'en til filter for hierarkiidentitet aktiverer visualiseringer, der bruger Matrix DataView-tilknytning til at filtrere data på flere felter ad gangen baseret på datapunkter, der bruger en hierarkistruktur.

Denne API er nyttig i følgende scenarier:

  • Filtrering af hierarkier baseret på datapunkter
  • Brugerdefinerede visualiseringer, der bruger semantiske modeller med gruppe på nøgler

Bemærk

Api'en til filter for hierarkiidentitet er tilgængelig fra API-version 5.9.0

Filtergrænsefladen vises i følgende kode:

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

  • filterType: FilterType.HierarchyIdentity (nedarvet fra IFilter)

  • target: Matrix af relevante kolonner i forespørgslen. I øjeblikket understøttes kun en enkelt rolle. Derfor er målet ikke påkrævet og skal være tomt.

  • hierarchyData: de markerede og ikke-markerede elementer i et hierarkitræ, hvor hver IHierarchyIdentityFilterNode<IdentityType> repræsenterer en enkelt værdivalg.

TypeScript
type IHierarchyIdentityFilterTarget = IQueryNameTarget[]

interface IQueryNameTarget {
    queryName: string;
}
  • queryName: forespørgselsnavnet på kildekolonnen i forespørgslen. Det kommer fra DataViewMetadataColumn
TypeScript
interface IHierarchyIdentityFilterNode<IdentityType> {
    identity: IdentityType;
    children?: IHierarchyIdentityFilterNode<IdentityType>[];
    operator: HierarchyFilterNodeOperators;
}
  • identity: Node-identiteten i DataView. Skal IdentityType være CustomVisualOpaqueIdentity

  • underordnede: Liste over underordnede noder, der er relevante for den aktuelle markering

  • operator: Operatoren for enkelte objekter i træet. Operatoren kan være en af følgende tre indstillinger:

    TypeScript
    type HierarchyFilterNodeOperators = "Selected" | "NotSelected" | "Inherited";
    
    • Selected: værdien er eksplicit valgt.

    • NotSelected: Værdien er eksplicit ikke valgt.

    • Nedarvet: valg af værdi er i overensstemmelse med den overordnede værdi i hierarkiet eller standard, hvis det er rodværdien.

Vær opmærksom på følgende regler, når du definerer dit hierarkiidentitetsfilter:

  • Tag identiteterne fra DataView.
  • Hver identitetssti skal være en gyldig sti i DataView.
  • Alle blade skal have operatoren Selected eller NotSelected.
  • Brug funktionen til ICustomVisualsOpaqueUtils.compareCustomVisualOpaqueIdentities at sammenligne identiteter.
  • Identiteterne kan ændre følgende ændringer i felter (f.eks. tilføjelse eller fjernelse af felter). Power BI tildeler de opdaterede identiteter til det eksisterende filter.hierarchyData.

Sådan bruger du API'en til hierarkiets identitetsfilter

Følgende kode er et eksempel på, hvordan du bruger API'en til hierarkiidentitetsfilter i en brugerdefineret visualisering:

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

Hvis du vil anvende filteret, skal du bruge applyJsonFilter API-kaldet:

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

Hvis du vil gendanne det aktive JSON-filter, skal du bruge den jsonFilters egenskab, der blev fundet i "VisualUpdateOptions":

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

Filteret HierarchyIdnetity understøttes kun for hierarkisk relaterede felter. Power BI validerer som standard ikke, om felterne er hierarkisk relaterede.

Hvis du vil aktivere hierarkisk relateret validering, skal du føje egenskaben 'areHierarchically Related' til den relevante rollebetingelse i capabilities.json-filen:

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

Felter er hierarkisk relaterede, hvis følgende betingelser er opfyldt:

  • Ingen inkluderet relationskant er mange til mange-kardinalitet eller ConceptualNavigationBehavior.Weak.

  • Alle felter i filteret findes i stien.

  • Alle relationer i stien har samme retning eller tovejs.

  • Relationsretningen svarer til kardinaliteten for en til mange eller tovejs.

Eksempel på hierarkirelationer

F.eks. på grund af følgende objektrelation:

Diagram, der viser filterets tovejs karakter.

  • A, B er hierarkisk relateret: true
  • B, C er hierarkisk relateret: sand
  • A, B, C er hierarkisk relateret: true
  • A, C, E er hierarkisk relateret: sand (A -> E -> C)
  • A, B, E er hierarkisk relateret: sand (B -> A -> E)
  • A, B, C, E er hierarkisk relateret: sand (B -> A -> E -> C)
  • A, B, C, D er hierarkisk relateret: falsk (overtrådt regel #3)
  • C, D er hierarkisk relateret: true
  • B, C, D er hierarkisk relateret: falsk (overtrådt regel #3)
  • A, C, D, E er hierarkisk relateret: falsk (overtrådt regel #3)

Bemærk

  • Når disse valideringer er aktiveret, og felterne ikke er hierarkisk relaterede, gengives visualiseringen ikke, og der vises en fejlmeddelelse:

    Skærmbillede af visualisering med valideringer aktiveret, der ikke kunne indlæses, fordi felterne ikke er hierarkisk relaterede. Fejlmeddelelsen siger

    Skærmbillede af fejlmeddelelse, når valideringer er aktiveret, og felterne ikke er hierarkisk relaterede. I meddelelsen står der

  • Når disse valideringer er deaktiveret, og filtervisualiseringen anvender et filter, der indeholder noder, der er relateret til ikke-hierarkisk relaterede felter, gengives andre visualiseringer muligvis ikke korrekt, når målingerne er i brug:

    Skærmbillede af visualisering med valideringer, der ikke kunne indlæses, fordi felterne ikke er hierarkisk relaterede. Fejlmeddelelsen siger

    Skærmbillede af fejlmeddelelse, når valideringer er deaktiveret, og felterne ikke er hierarkisk relaterede. Meddelelsen siger

Kodeeksempel til opdatering af hierarkidatatræet efter ny markering

Følgende kode viser, hvordan du opdaterer hierarchyData træet efter en ny markering:

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

Overvejelser og begrænsninger

  • Dette filter understøttes kun for matrixdataVisningstilknytning.

  • Visualiseringen må kun indeholde én grupperingsdatarolle.

  • En visualisering, der bruger filtertypen Hierarkiidentitet, bør kun anvende et enkelt filter af denne type.