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[];
}
Overenie polí súvisiacich s hierarchiou (voliteľné)
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:
- 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:
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ť:
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.