Jaa


Hierarkkisten käyttäjätietojen suodattimien ohjelmointirajapinta Power BI:n visualisoinneissa

Hierarkian käyttäjätietojen suodattimen ohjelmointirajapinnan avulla visualisoinnit, jotka käyttävät Matriisitietonäkymän yhdistämismääritystä, voivat suodattaa tietoja useista kentistä kerralla hierarkiarakennetta käyttävien arvopisteiden perusteella.

Tästä ohjelmointirajapinnasta on hyötyä seuraavissa tilanteissa:

  • Hierarkioiden suodattaminen arvopisteiden perusteella
  • Mukautetut visualisoinnit, jotka käyttävät semanttisia malleja ja ryhmä avaimia

Muistiinpano

Hierarkian käyttäjätietojen suodattimen ohjelmointirajapinta on käytettävissä ohjelmointirajapinnan versiosta 5.9.0 alkaen

Suodatinliittymä näkyy seuraavassa koodissa:

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

  • filterType: FilterType.HierarchyIdentity (peritty IFilter-kohteesta)

  • target: Kyselyn asiaankuuluvien sarakkeiden matriisi. Tällä hetkellä tuetaan vain yhtä roolia. siksi kohdetta ei tarvita, ja sen pitäisi olla tyhjä.

  • hierarchyData: valitut ja valitsemattomat kohteet hierarkiapuussa, jossa kukin IHierarchyIdentityFilterNode<IdentityType> edustaa yksittäistä arvon valintaa.

type IHierarchyIdentityFilterTarget = IQueryNameTarget[]

interface IQueryNameTarget {
    queryName: string;
}
  • queryName: kyselyn lähdesarakkeen kyselyn nimi. Se on peräisin DataViewMetadataColumn
interface IHierarchyIdentityFilterNode<IdentityType> {
    identity: IdentityType;
    children?: IHierarchyIdentityFilterNode<IdentityType>[];
    operator: HierarchyFilterNodeOperators;
}
  • identity: DataView-solmun käyttäjätiedot. Pitäisi IdentityType olla CustomVisualOpaqueIdentity

  • aliluettelo nykyiseen valintaan liittyvistä solmun aliluetteloista

  • -operaattori: Puun yksittäisten objektien operaattori. -operaattori voi olla yksi seuraavista kolmesta vaihtoehdosta:

    type HierarchyFilterNodeOperators = "Selected" | "NotSelected" | "Inherited";
    
    • Valittu: arvo on valittu eksplisiittisesti.

    • Ei valittu: arvoa ei ole valittu.

    • Peritty: arvon valinta perustuu hierarkian pääarvoon tai oletusarvoon, jos se on pääarvo.

Ota seuraavat säännöt huomioon hierarkian käyttäjätietojen suodatinta määritettäessä:

  • Tutustu DataView'n käyttäjätietoihin.
  • Kunkin käyttäjätietopolun on oltava kelvollinen polku DataView-kohteessa.
  • Jokaisella lehdillä tulee olla operaattori valittuna tai Ei valittuna.
  • Voit vertailla käyttäjätietoja käyttämällä -funktiota ICustomVisualsOpaqueUtils.compareCustomVisualOpaqueIdentities .
  • Käyttäjätiedot saattavat muuttua seuraavien kenttien muutosten mukaisesti (esimerkiksi lisäämällä tai poistamalla kenttiä). Power BI määrittää päivitetyt käyttäjätiedot olemassa olevalle filter.hierarchyData-tiedolle.

Hierarkian käyttäjätietojen suodattimen ohjelmointirajapinnan käyttäminen

Seuraava koodi on esimerkki siitä, miten voit käyttää hierarkian käyttäjätietojen suodattimen ohjelmointirajapintaa mukautetussa visualisoinnissa:

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

Voit käyttää suodatinta ohjelmointirajapinnan kutsulla applyJsonFilter :

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

Voit palauttaa aktiivisen JSON-suodattimen jsonFilters käyttämällä "VisualUpdateOptions"-ominaisuuden avulla seuraavaa ominaisuutta:

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

HierarchyIdnetity Suodatinta tuetaan vain hierarkkisissa kentissä. Oletusarvoisesti Power BI ei vahvista, liittyvätkö kentät hierarkkisesti toisiinsa.

Aktivoi hierarkkisesti liittyvä vahvistus lisäämällä areHierarchicallyRelated-ominaisuus capabilities.json tiedoston asianmukaiseen rooliehtoon:

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

Kentät liittyvät hierarkkisesti toisiinsa, jos seuraavat ehdot täyttyvät:

  • Sisältyneen suhteen reuna on monta moneen -kardinaliteetti, tai ConceptualNavigationBehavior.Weak.

  • Kaikki suodattimen kentät ovat olemassa polussa.

  • Jokainen polun suhde on samansuuntainen tai kaksisuuntainen.

  • Suhteen suunta vastaa kardinaliteettia, joka on yksi moneen tai kaksisuuntainen.

Esimerkki hierarkiasuhteista

Otetaan esimerkiksi seuraava entiteettisuhde:

Kaavio, joka näyttää suodattimen kaksisuuntaisen luonteen.

  • A, B liittyvät hierarkkisesti toisiinsa: true
  • B, C liittyvät hierarkkisesti toisiinsa: true
  • A, B, C liittyvät hierarkkisesti: true
  • A, C, E liittyvät hierarkkisesti: tosi (A --> E --> C)
  • A, B, E liittyvät hierarkkisesti: true (B --> A --> E)
  • A, B, C, E liittyvät hierarkkisesti: true (B --> A --> E --> C)
  • A, B, C, D liittyvät hierarkkisesti: false (rikkoi sääntöä 3)
  • C, D liittyvät hierarkkisesti toisiinsa: true
  • B, C, D liittyvät hierarkkisesti: false (rikkoi sääntöä 3)
  • A, C, D, E liittyvät hierarkkisesti toisiinsa: false (rikkoi sääntöä 3)

Muistiinpano

  • Kun nämä vahvistukset ovat käytössä eivätkä kentät ole hierarkkisesti yhteydessä toisiinsa, visualisointia ei hahmonneta ja näyttöön tulee virhesanoma:

    Näyttökuva visualisoinnista, jossa vahvistukset ovat käytössä, koska kenttien lataaminen ei liity hierarkkisesti toisiinsa. Virhesanomassa lukee

    Näyttökuva virhesanomasta, kun vahvistukset ovat käytössä eivätkä kentät ole hierarkkisesti yhteydessä toisiinsa. Viestissä sanotaan, ettei tätä visualisointia voi näyttää.

  • Kun nämä vahvistukset on poistettu käytöstä ja suodatinvisualisointi käyttää suodatinta, joka sisältää ei-hierarkkisesti toisiinsa liittyviin kenttiin liittyviä solmuja, muita visualisointeja ei ehkä hahmonneta oikein, kun mittarit ovat käytössä:

    Näyttökuva visualisoinnista, jossa vahvistukset on poistettu käytöstä, koska kenttien lataaminen ei liity hierarkkisesti toisiinsa. Virhesanomassa lukee

    Näyttökuva virhesanomasta, kun vahvistukset on poistettu käytöstä ja kentät eivät ole hierarkkisesti yhteydessä toisiinsa. Viestissä sanotaan, ettei tietoja voitu ladata tälle visualisoinnille.

Koodiesimerkki hierarkiatietopuun päivittämiseen uuden valinnan jälkeen

Seuraava koodi näyttää, miten puu päivitetään hierarchyData uuden valinnan jälkeen:

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

Huomioitavat asiat ja rajoitukset

  • Tätä suodatinta tuetaan vain matriisin dataView-yhdistämismäärityksessä.

  • Visualisoinnin tulee sisältää vain yksi ryhmittelytietorooli.

  • Visualisoinnissa, joka käyttää hierarkian käyttäjätietojen suodatintyyppiä, tulisi käyttää vain yhtä tämäntyyppistä suodatinta.