De API voor hiërarchische identiteitfilters in Power BI-visuals
Met de Api voor hiërarchie-identiteitsfilters kunnen visuals die matrixgegevenstoewijzing gebruiken om gegevens op meerdere velden tegelijk te filteren op basis van gegevenspunten die gebruikmaken van een hiërarchiestructuur.
Deze API is handig in de volgende scenario's:
- Hiërarchieën filteren op basis van gegevenspunten
- Aangepaste visuals die gebruikmaken van semantische modellen met groep op sleutels
Notitie
De hiërarchieidentiteitsfilter-API is beschikbaar via API-versie 5.9.0
De filterinterface wordt weergegeven in de volgende code:
interface IHierarchyIdentityFilter<IdentityType> extends IFilter {
target: IHierarchyIdentityFilterTarget;
hierarchyData: IHierarchyIdentityFilterNode<IdentityType>[];
}
$schema:
https://powerbi.com/product/schema#hierarchyIdentity
(overgenomen van IFilter)filterType: FilterType.HierarchyIdentity (overgenomen van IFilter)
doel: Matrix van relevante kolommen in de query. Momenteel wordt slechts één rol ondersteund; Daarom is het doel niet vereist en moet het leeg zijn.
hierarchyData: de geselecteerde en niet-geselecteerde items in een hiërarchiestructuur waarin elk
IHierarchyIdentityFilterNode<IdentityType>
één waardeselectie vertegenwoordigt.
type IHierarchyIdentityFilterTarget = IQueryNameTarget[]
interface IQueryNameTarget {
queryName: string;
}
- queryName: querynaam van de bronkolom in de query. Het komt van de
DataViewMetadataColumn
interface IHierarchyIdentityFilterNode<IdentityType> {
identity: IdentityType;
children?: IHierarchyIdentityFilterNode<IdentityType>[];
operator: HierarchyFilterNodeOperators;
}
identiteit: De knooppuntidentiteit in DataView. De
IdentityType
moet zijnCustomVisualOpaqueIdentity
onderliggende items: Lijst met knooppunten die relevant zijn voor de huidige selectie
operator: De operator voor afzonderlijke objecten in de structuur. De operator kan een van de volgende drie opties zijn:
type HierarchyFilterNodeOperators = "Selected" | "NotSelected" | "Inherited";
Geselecteerd: waarde is expliciet geselecteerd.
NotSelected: waarde is expliciet niet geselecteerd.
Overgenomen: waardeselectie is afhankelijk van de bovenliggende waarde in de hiërarchie of standaard als dit de hoofdwaarde is.
Houd rekening met de volgende regels bij het definiëren van uw hiërarchie-identiteitsfilter:
- Neem de identiteiten uit de DataView.
- Elk identiteitspad moet een geldig pad in de DataView zijn.
- Elk blad moet een operator van Geselecteerd of Niet geselecteerd hebben.
- Gebruik de
ICustomVisualsOpaqueUtils.compareCustomVisualOpaqueIdentities
functie om identiteiten te vergelijken. - De identiteiten kunnen de volgende velden wijzigen (bijvoorbeeld velden toevoegen of verwijderen). Power BI wijst de bijgewerkte identiteiten toe aan de bestaande filter.hierarchyData.
De API voor het filteren van hiërarchie-identiteiten gebruiken
De volgende code is een voorbeeld van het gebruik van de hiërarchie-id-filter-API in een aangepaste visual:
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();
Gebruik de applyJsonFilter
API-aanroep om het filter toe te passen:
this.host.applyJsonFilter(filter, "general", "filter", action);
Als u het actieve JSON-filter wilt herstellen, gebruikt u de jsonFilters
eigenschap in VisualUpdateOptions:
export interface VisualUpdateOptions extends extensibility.VisualUpdateOptions {
//...
jsonFilters?: IFilter[];
}
Validatie van gerelateerde hiërarchievelden (optioneel)
Het HierarchyIdnetity
filter wordt alleen ondersteund voor hiërarchisch gerelateerde velden. Power BI valideert standaard niet of de velden hiërarchisch zijn gerelateerd.
Als u hiërarchisch gerelateerde validatie wilt activeren, voegt u de eigenschap 'areHierarchically Related' toe aan de relevante rolvoorwaarde in het bestand capabilities.json:
"dataViewMappings": [
{
"conditions": [
{
"Rows": {
"min": 1,
"areHierarchicallyRelated": true <------ NEW ------>
},
"Value": {
"min": 0
}
}
],
...
}
]
Velden zijn hiërarchisch gerelateerd als aan de volgende voorwaarden wordt voldaan:
Geen opgenomen relatierand is veel tot veel kardinaliteit, noch
ConceptualNavigationBehavior.Weak
.Alle velden in het filter bestaan in het pad.
Elke relatie in het pad heeft dezelfde richting of bidirectioneel.
De relatierichting komt overeen met de kardinaliteit voor één tot veel of twee richtingen.
Voorbeeld van hiërarchierelaties
Bijvoorbeeld, op basis van de volgende entiteitsrelatie:
- A, B zijn hiërarchisch gerelateerd: true
- B, C zijn hiërarchisch gerelateerd: true
- A, B, C zijn hiërarchisch gerelateerd: true
- A, C, E zijn hiërarchisch gerelateerd: waar (A --> E --> C)
- A, B, E zijn hiërarchisch gerelateerd: true (B --> A --> E)
- A, B, C, E zijn hiërarchisch gerelateerd: true (B --> A --> E --> C)
- A, B, C, D zijn hiërarchisch gerelateerd: false (geschonden regel 3)
- C, D zijn hiërarchisch gerelateerd: true
- B, C, D zijn hiërarchisch gerelateerd: onwaar (geschonden regel 3)
- A, C, D, E zijn hiërarchisch gerelateerd: onwaar (geschonden regel 3)
Notitie
Wanneer deze validaties zijn ingeschakeld en de velden niet hiërarchisch zijn gerelateerd, wordt de visual niet weergegeven en wordt er een foutbericht weergegeven:
Wanneer deze validaties zijn uitgeschakeld en de filtervisual een filter toepast dat knooppunten bevat die betrekking hebben op niet-hiërarchisch gerelateerde velden, worden andere visuals mogelijk niet goed weergegeven wanneer metingen worden gebruikt:
Codevoorbeeld voor het bijwerken van de gegevensstructuur van de hiërarchie na nieuwe selectie
De volgende code laat zien hoe u de hierarchyData
structuur bijwerkt na een nieuwe selectie:
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();
}
Overwegingen en beperkingen
Dit filter wordt alleen ondersteund voor matrixgegevensweergavetoewijzing.
De visual mag slechts één gegevensrol groeperen bevatten.
Een visual die gebruikmaakt van het filtertype Hiërarchie-identiteit moet slechts één filter van dit type toepassen.