--- title: "Working with Semantic Models (DAX queries)" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Working with Semantic Models (DAX queries)} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE) ``` A semantic model is the report-ready data behind Power BI and Fabric reports. It combines tables with relationships, measures, and business calculations. From R, you can query that model with DAX and refresh it after its source data changes. Start by querying a small result. Refresh and monitoring come next, followed by the more specialized controls used for production models. This guide uses a discovered `FabricSemanticModel` R6 object. Read its service fields directly. `$dax_query()` corresponds to `fabric_pbi_dax_query()`, while the `$refresh*()` methods correspond to the `fabric_pbi_refresh*()` function family: ```{r, eval = FALSE} library(fabricQueryR) model <- fabric_semantic_models("Analytics workspace")[[1]] ``` ## Query the model with DAX DAX is the query and calculation language used by semantic models. Use `$dax_query()` (`fabric_pbi_dax_query()`) to run a query. It normally begins with `EVALUATE` and returns a table. If you are new to DAX, create and test a query in the model's *DAX query view* in Fabric, then use the same text from R: ```{r, eval = FALSE} rows <- model$dax_query( dax = "EVALUATE TOPN(100, 'Customers')" ) head(rows) ``` Query the semantic model when you want its measures and relationships, or need the same business definition used by a report. Query the Warehouse or Lakehouse instead when you need the underlying source rows and SQL is a better fit. For example, call `$dax_query()` (`fabric_pbi_dax_query()`) to group a DAX measure by a model column: ```{r, eval = FALSE} sales <- model$dax_query( dax = paste( "EVALUATE", "SUMMARIZECOLUMNS(", " 'Region'[Region],", " \"Total Sales\", [Total Sales]", ")" ) ) ``` The normal JSON mode returns one tibble and is the simplest option. The later `api = "arrow"` mode is useful for richer types, multiple result tables, or a stream, but requires a capacity-backed model and additional tenant settings. The `timeout` argument bounds the client HTTP request. For Arrow queries, `arrow_options$queryTimeout` separately controls the server-side query limit. ## Check access before refreshing Querying normally requires Read and Build permission on the model and the tenant setting that enables Execute Queries. Refreshing requires Write access. If a query succeeds but a refresh does not, ask the model owner or Fabric administrator to check those permissions and tenant settings. ## Refresh after an upstream update A standard refresh starts with `$refresh()` (`fabric_pbi_refresh()`) and waits with `$refresh_wait()` (`fabric_pbi_refresh_wait()`): ```{r, eval = FALSE} refresh <- model$refresh() completed <- model$refresh_wait(refresh, timeout = 1800) completed$state completed$start_time completed$end_time ``` Pass the returned handle to status, wait, or cancel functions without copying IDs. If you save it and restore it in another R process, authenticate again. For example, a data-load workflow can use the Lakehouse `$write_table()` method (`fabric_lakehouse_write_table()`) and refresh only after the source update has committed successfully. Here `lakehouse` is a discovered Lakehouse containing the model's source table, and `new_sales` is the data frame to load: ```{r, eval = FALSE} load <- lakehouse$write_table( table = "Sales", data = new_sales, schema = "dbo", mode = "overwrite" ) refresh <- model$refresh(mode = "enhanced", type = "Full") completed <- model$refresh_wait( refresh, timeout = 1800, cancel_on_timeout = TRUE ) ``` Client-side `timeout` in `$refresh_wait()` (`fabric_pbi_refresh_wait()`) only bounds how long R waits. Without `cancel_on_timeout = TRUE`, the Power BI refresh keeps running. Use `type = "Full"` to re-import updated source data and recalculate the model. Enhanced refresh defaults to `"Automatic"`, which processes partitions whose state is not Ready; a previously processed Import model can remain unchanged after its source data changes. See Microsoft's [processing-type definitions](https://learn.microsoft.com/en-us/rest/api/power-bi/datasets/refresh-dataset-in-group#datasetrefreshtype). ## Use enhanced refresh controls Enhanced refresh through `$refresh()` (`fabric_pbi_refresh()`) requires Power BI Premium, Premium per user, Embedded, or Fabric capacity. Supplying an enhanced option selects enhanced mode automatically, or set `mode = "enhanced"` explicitly: ```{r, eval = FALSE} refresh <- model$refresh( mode = "enhanced", type = "Full", objects = list( list(table = "Sales", partition = "2026"), list(table = "Calendar") ), commit_mode = "Transactional", max_parallelism = 4L, retry_count = 1L, timeout = "02:00:00" ) completed <- model$refresh_wait(refresh, timeout = 5 * 60 * 60) ``` The submission `timeout` controls an individual Power BI attempt; the numeric timeout in `$refresh_wait()` (`fabric_pbi_refresh_wait()`) controls how long R waits. Use `"Transactional"` when the previous model should remain available unless the complete refresh succeeds. See `?fabric_pbi_refresh` before using partial-batch commits or refresh-policy options. ## Diagnose attempts and failures Use `$refresh_status()` (`fabric_pbi_refresh_status()`) when a refresh takes longer than expected or fails: ```{r, eval = FALSE} status <- model$refresh_status(refresh) status$state status$attempts status$messages status$service_error status$objects status$details_url ``` The normalized state distinguishes work that is queued, running, completed, completed with warnings, failed, timed out, cancelled, or disabled. Attempt details and `details_url` help diagnose the service response; the complete response remains available in `raw`. By default, wait raises typed R conditions for service failures, cancellation, service timeouts, and disabled refreshes. Keep the terminal detail instead when building a monitoring table by calling `$refresh_wait()` (`fabric_pbi_refresh_wait()`) with `error_on_failure = FALSE`: ```{r, eval = FALSE} result <- model$refresh_wait( refresh, error_on_failure = FALSE ) if (result$state != "Completed") result$details_url ``` ## Inspect history Inspect recent refreshes with `$refresh_history()` (`fabric_pbi_refresh_history()`) when you need to compare duration or failure patterns: ```{r, eval = FALSE} history <- model$refresh_history(top = 10L) history[[1]]$refresh_type history[[1]]$state history[[1]]$attempts # Refresh an old history entry from its request ID and stored model context latest <- model$refresh_status(history[[1]]) ``` Here `$refresh_status()` calls `fabric_pbi_refresh_status()`, just as it does for a newly submitted refresh. ## More information See Microsoft's documentation for more information: - [Refresh Dataset API](https://learn.microsoft.com/en-us/rest/api/power-bi/datasets/refresh-dataset-in-group) - [Enhanced refresh](https://learn.microsoft.com/en-us/power-bi/connect-data/asynchronous-refresh) - [Refresh execution details](https://learn.microsoft.com/en-us/rest/api/power-bi/datasets/get-refresh-execution-details-in-group) - [Data refresh and capacity limits](https://learn.microsoft.com/en-us/power-bi/connect-data/refresh-data) - [How Direct Lake works](https://learn.microsoft.com/en-us/fabric/fundamentals/direct-lake-how-it-works)