Dela via


Den hierarkiska identiteten filtrerar API i visuella Power BI-objekt

API:et för hierarkiidentitetsfilter gör det möjligt för visuella objekt som använder Matrisdatavymappning att filtrera data på flera fält i taget baserat på datapunkter som använder en hierarkistruktur.

Det här API:et är användbart i följande scenarier:

  • Filtrera hierarkier baserat på datapunkter
  • Anpassade visuella objekt som använder semantiska modeller med grupp på nycklar

Kommentar

API:et för hierarkiidentitetsfilter är tillgängligt från API-version 5.9.0

Filtergränssnittet visas i följande kod:

interface IHierarchyIdentityFilter<IdentityType> extends IFilter {
    target: IHierarchyIdentityFilterTarget;
    hierarchyData: IHierarchyIdentityFilterNode<IdentityType>[];
}
  • $schema: https://powerbi.com/product/schema#hierarchyIdentity (ärvd från IFilter)

  • filterType: FilterType.HierarchyIdentity (ärvd från IFilter)

  • target: Matris med relevanta kolumner i frågan. För närvarande stöds endast en enda roll. Målet är därför inte obligatoriskt och bör vara tomt.

  • hierarchyData: de markerade och omarkerade objekten i ett hierarkiträd där var och IHierarchyIdentityFilterNode<IdentityType> en representerar en enda värdemarkering.

type IHierarchyIdentityFilterTarget = IQueryNameTarget[]

interface IQueryNameTarget {
    queryName: string;
}
  • queryName: frågenamnet för källkolumnen i frågan. Den kommer från DataViewMetadataColumn
interface IHierarchyIdentityFilterNode<IdentityType> {
    identity: IdentityType;
    children?: IHierarchyIdentityFilterNode<IdentityType>[];
    operator: HierarchyFilterNodeOperators;
}
  • identitet: Nodidentiteten i DataView. Bör IdentityType vara CustomVisualOpaqueIdentity

  • underordnade: Lista över nod underordnade som är relevanta för den aktuella markeringen

  • operator: Operatorn för enskilda objekt i trädet. Operatorn kan vara något av följande tre alternativ:

    type HierarchyFilterNodeOperators = "Selected" | "NotSelected" | "Inherited";
    
    • Valt: värdet är uttryckligen markerat.

    • NotSelected: värdet är uttryckligen inte markerat.

    • Ärvd: värdeval är enligt det överordnade värdet i hierarkin, eller standard om det är rotvärdet.

Tänk på följande regler när du definierar hierarkiidentitetsfiltret:

  • Ta identiteterna från DataView.
  • Varje identitetssökväg bör vara en giltig sökväg i DataView.
  • Varje löv bör ha en operator för Selected eller NotSelected.
  • Använd funktionen för ICustomVisualsOpaqueUtils.compareCustomVisualOpaqueIdentities att jämföra identiteter.
  • Identiteterna kan ändras efter fältändringar (till exempel att lägga till eller ta bort fält). Power BI tilldelar de uppdaterade identiteterna till befintliga filter.hierarchyData.

Så här använder du API:et för hierarkiidentitetsfilter

Följande kod är ett exempel på hur du använder API:et för hierarkiidentitetsfilter i ett anpassat visuellt objekt:

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

Använd API-anropet för applyJsonFilter att tillämpa filtret:

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

Om du vill återställa det aktiva JSON-filtret använder du jsonFilters egenskapen som finns i "VisualUpdateOptions":

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

HierarchyIdnetity Filtret stöds endast för hierarkiskt relaterade fält. Som standard verifierar Inte Power BI om fälten är hierarkiskt relaterade.

Om du vill aktivera hierarkiskt relaterad validering lägger du till egenskapen "areHierarchicallyRelated" i det relevanta rollvillkoret i capabilities.json-filen:

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

Fält är hierarkiskt relaterade om följande villkor uppfylls:

  • Ingen inkluderad relationsgräns är många till många kardinaliteter eller ConceptualNavigationBehavior.Weak.

  • Alla fält i filtret finns i sökvägen.

  • Varje relation i sökvägen har samma riktning eller dubbelriktad.

  • Relationsriktningen matchar kardinaliteten för en till många eller dubbelriktad.

Exempel på hierarkirelationer

Till exempel med tanke på följande entitetsrelation:

Diagram som visar filtrets dubbelriktade karaktär.

  • A, B är hierarkiskt relaterade: true
  • B, C är hierarkiskt relaterade: true
  • A, B, C är hierarkiskt relaterade: true
  • A, C, E är hierarkiskt relaterade: true (A --> E --> C)
  • A, B, E är hierarkiskt relaterade: true (B --> A --> E)
  • A, B, C, E är hierarkiskt relaterade: true (B -- A -->> E --> C)
  • A, B, C, D är hierarkiskt relaterade: false (brutit mot regel 3)
  • C, D är hierarkiskt relaterade: true
  • B, C, D är hierarkiskt relaterade: false (brutit mot regel 3)
  • A, C, D, E är hierarkiskt relaterade: false (brutit mot regel 3)

Kommentar

  • När dessa valideringar är aktiverade och fälten inte är hierarkiskt relaterade återges inte det visuella objektet och ett felmeddelande visas:

    Skärmbild av visuellt objekt med valideringar aktiverade som inte kan läsas in eftersom fälten inte är hierarkiskt relaterade. Felmeddelandet säger

    Skärmbild av felmeddelande när valideringar är aktiverade och fälten inte är hierarkiskt relaterade. Meddelandet säger

  • När dessa valideringar är inaktiverade och det visuella filterobjektet tillämpar ett filter som innehåller noder som är relaterade till icke-hierarkiskt relaterade fält, kanske andra visuella objekt inte återges korrekt när mått används:

    Skärmbild av visuellt objekt med valideringar inaktiverade som inte kan läsas in eftersom fälten inte är hierarkiskt relaterade. Felmeddelandet säger

    Skärmbild av felmeddelande när valideringar inaktiveras och fälten inte är hierarkiskt relaterade. Meddelandet säger

Kodexempel för att uppdatera hierarkidataträdet efter ny markering

Följande kod visar hur du uppdaterar hierarchyData trädet efter en ny markering:

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

Beaktanden och begränsningar

  • Det här filtret stöds endast för matrisdataVymappning.

  • Det visuella objektet får bara innehålla en grupperingsdataroll.

  • Ett visuellt objekt som använder filtertypen Hierarkiidentitet bör endast använda ett enda filter av den här typen.