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
varaCustomVisualOpaqueIdentity
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[];
}
Validering av hierarkirelaterade fält (valfritt)
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:
- 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:
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:
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();
}