事件
3月31日 下午11時 - 4月2日 下午11時
最終Microsoft Fabric、Power BI、SQL 和 AI 社群主導的活動。 2025 年 3 月 31 日至 4 月 2 日。
立即註冊階層身分識別篩選 API 可讓使用矩陣 DataView 對應的視覺效果,根據使用階層結構的資料點,一次篩選多個欄位上的資料。
此 API 在下列案例中很有用:
注意
階層身分識別篩選 API 可從 API 版本 5.9.0 取得
篩選介面如下列程式碼所示:
interface IHierarchyIdentityFilter<IdentityType> extends IFilter {
target: IHierarchyIdentityFilterTarget;
hierarchyData: IHierarchyIdentityFilterNode<IdentityType>[];
}
$schema:https://powerbi.com/product/schema#hierarchyIdentity
(繼承自 IFilter)
filterType:FilterType.HierarchyIdentity (繼承自 IFilter)
目標:查詢中相關資料行的陣列。 目前僅支援單一角色;因此,不需要目標,且應該是空的。
hierarchyData:階層樹狀結構中已選取和未選取的項目,其中每個 IHierarchyIdentityFilterNode<IdentityType>
都代表單一值選取項目。
type IHierarchyIdentityFilterTarget = IQueryNameTarget[]
interface IQueryNameTarget {
queryName: string;
}
DataViewMetadataColumn
interface IHierarchyIdentityFilterNode<IdentityType> {
identity: IdentityType;
children?: IHierarchyIdentityFilterNode<IdentityType>[];
operator: HierarchyFilterNodeOperators;
}
身分識別:DataView 中的節點身分識別。 IdentityType
應該是 CustomVisualOpaqueIdentity
子系:與目前選取項目相關的節點子系清單
運算子:樹狀結構中單一物件的運算子。 運算子可以是下列三個選項之一:
type HierarchyFilterNodeOperators = "Selected" | "NotSelected" | "Inherited";
Selected:已明確選取值。
NotSelected:未明確選取值。
Inherited:根據階層中的父代值選取值,或者若為根值則使用預設值。
定義階層身分識別篩選時,請記住下列規則:
ICustomVisualsOpaqueUtils.compareCustomVisualOpaqueIdentities
函式。下列程式碼是如何在自訂視覺效果中使用階層身分識別篩選 API 的範例:
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();
若要套用篩選,請使用 applyJsonFilter
API 呼叫:
this.host.applyJsonFilter(filter, "general", "filter", action);
若要還原使用中的 JSON 篩選,請使用 "VisualUpdateOptions" 中找到的 jsonFilters
屬性:
export interface VisualUpdateOptions extends extensibility.VisualUpdateOptions {
//...
jsonFilters?: IFilter[];
}
只有階層式相關欄位支援 HierarchyIdnetity
篩選。 根據預設,Power BI 不會驗證欄位是否為階層式相關。
若要啟用階層式相關驗證,請將 'areHierarchicallyRelated' 屬性新增至 capabilities.json 檔案中的相關角色條件:
"dataViewMappings": [
{
"conditions": [
{
"Rows": {
"min": 1,
"areHierarchicallyRelated": true <------ NEW ------>
},
"Value": {
"min": 0
}
}
],
...
}
]
如果符合下列條件,欄位會以階層方式相關:
包含的關聯性邊緣不是多對多基數,也不是 ConceptualNavigationBehavior.Weak
。
篩選中的所有欄位都存在於路徑中。
路徑中的每個關聯性都有相同的方向或雙向。
關聯性方向會比對一對多或雙向的基數。
例如,假設有下列實體關聯性:
注意
啟用這些驗證,且欄位不是階層式相關時,視覺效果不會呈現,而且會顯示錯誤訊息:
停用這些驗證時,篩選視覺效果套用的篩選包含與非階層式相關欄位相關的節點,而正在使用量值時,可能無法正確呈現其他視覺效果:
下列程式碼示範如何在新的選取項目之後更新 hierarchyData
樹狀結構:
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();
}
只有矩陣 dataView 對應支援此篩選。
視覺效果應該只包含 群組 資料角色。
使用階層身分識別篩選類型的視覺效果應該只套用此類型的單一篩選。
事件
3月31日 下午11時 - 4月2日 下午11時
最終Microsoft Fabric、Power BI、SQL 和 AI 社群主導的活動。 2025 年 3 月 31 日至 4 月 2 日。
立即註冊