@@ -101,131 +101,183 @@ get_column_def <- function(name, field, value) {
101
101
)
102
102
}
103
103
104
- dataview_table <- function (data , start = 0 , end = NULL , sortModel = NULL , filterModel = NULL ) {
105
-
106
- if (is.matrix(data )) {
107
- data <- as.data.frame.matrix(data )
108
- }
109
- if (! is.data.frame(data )) {
110
- stop(" data must be a data.frame or a matrix" )
111
- }
112
-
113
- data <- data.table :: copy(data )
114
- data.table :: setDT(data )
115
-
116
- data [, `:=`(" (row)" = numeric (), rowId = .I )]
117
- data.table :: setcolorder(data , neworder = c(" (row)" , " rowId" ), before = 1 )
118
-
119
- # number of rows & original column names
120
- .nrow <- nrow(data )
121
- .colnames <- colnames(data )
122
- if (is.null(.colnames )) {
123
- .colnames <- sprintf(" V%d" , seq_len(ncol(data )))
124
- } else {
125
- .colnames <- trimws(.colnames )
126
- }
127
-
128
- fields <- sprintf(" x%d" , seq_along(.colnames ))
129
- field_map <- setNames(.colnames , fields )
130
-
131
- if (! is.null(filterModel ) && length(filterModel ) > 0 ) {
104
+ dataview_table <- local({
105
+ cache_raw_dt <- NULL
106
+ cache_filtered_dt <- NULL
107
+ cache_dt <- NULL
108
+ cache_nrow <- NULL
109
+ cache_colnames <- NULL
110
+ cache_fields <- NULL
111
+ last_fm <- NULL
112
+ last_sm <- NULL
113
+
114
+ function (data , start = 0 , end = NULL ,
115
+ sortModel = NULL , filterModel = NULL ,
116
+ metadata_only = FALSE , force = FALSE ) {
117
+
118
+ key <- attr(data , " _dvkey" , exact = TRUE )
119
+ if (is.null(key )) key <- " <default>"
120
+
121
+ if (is.matrix(data )) {
122
+ data <- as.data.frame.matrix(data )
123
+ }
124
+ if (! is.data.frame(data )) {
125
+ stop(" data must be a data.frame/data.table or a matrix" )
126
+ }
132
127
133
- filter_strings <- lapply(names(filterModel ), function (fld ) {
134
- fd <- filterModel [[fld ]]
135
- col_name <- field_map [[fld ]]
128
+ # Metadata capture
129
+ .nrow <- nrow(data )
130
+ .colnames <- colnames(data )
131
+ if (is.null(.colnames )) {
132
+ .colnames <- sprintf(" V%d" , seq_len(ncol(data )))
133
+ } else {
134
+ .colnames <- trimws(.colnames )
135
+ }
136
+ fields <- sprintf(" x%d" , seq_len(length(.colnames ) + 2 ))
137
+ full_cols <- c(" (row)" , " rowId" , .colnames )
138
+ field_map <- setNames(full_cols , fields )
139
+
140
+ if (metadata_only ) {
141
+ meta_data <- data.table :: as.data.table(data [0 , ])
142
+ meta_data [, `:=`(" (row)" = numeric (), rowId = integer())]
143
+ data.table :: setcolorder(meta_data , neworder = c(" (row)" , " rowId" ), before = 1 )
144
+ columns <- .mapply(
145
+ get_column_def ,
146
+ list (full_cols , fields , meta_data ),
147
+ NULL
148
+ )
149
+ return (list (
150
+ columns = columns ,
151
+ rows = list (),
152
+ totalRows = .nrow ,
153
+ totalUnfiltered = .nrow
154
+ ))
155
+ }
136
156
137
- if (( ! is.null(fd $ type ) && ! is.null( fd $ filter )) ) {
157
+ if (is.null(cache_raw_dt [[ key ]]) || force ) {
138
158
139
- op <- fd $ type
140
- raw <- if (fd $ filterType == " date" ) fd $ dateFrom else fd $ filter
159
+ dt0 <- data.table :: as.data.table(data )
160
+ dt0 [, `:=`(" (row)" = numeric (), rowId = .I )]
161
+ data.table :: setcolorder(dt0 , neworder = c(" (row)" , " rowId" ), before = 1 )
141
162
142
- # quote or coerce the filter literal
143
- lit <- if (inherits(data [[col_name ]], " Date" )) {
144
- sprintf(' as.Date("%s")' , raw )
145
- } else if (is.numeric(data [[col_name ]])) {
146
- as.numeric(raw )
147
- } else if (is.logical(data [[col_name ]])) {
148
- as.logical(raw )
149
- } else {
150
- sprintf(' "%s"' , gsub(' "' , ' \\\\ "' , raw ))
151
- }
163
+ cache_raw_dt [[key ]] <<- dt0
164
+ cache_filtered_dt [[key ]] <<- NULL
165
+ cache_dt [[key ]] <<- NULL
166
+ cache_nrow [[key ]] <<- .nrow
167
+ cache_colnames [[key ]] <<- full_cols
168
+ cache_fields [[key ]] <<- fields
169
+ last_fm [[key ]] <<- NULL
170
+ last_sm [[key ]] <<- NULL
171
+ }
152
172
153
- # build the right comparison or string test
154
- expr <- switch (op ,
155
- equals = sprintf(' get("%s") == %s' , col_name , lit ),
156
- notEqual = sprintf(' get("%s") != %s' , col_name , lit ),
157
- greaterThan = sprintf(' get("%s") > %s' , col_name , lit ),
158
- greaterThanOrEqual = sprintf(' get("%s") >= %s' , col_name , lit ),
159
- lessThan = sprintf(' get("%s") < %s' , col_name , lit ),
160
- lessThanOrEqual = sprintf(' get("%s") <= %s' , col_name , lit ),
161
- contains = sprintf(' grepl(%s, get("%s"), fixed=TRUE)' , lit , col_name ),
162
- notContains = sprintf(' !grepl(%s, get("%s"), fixed=TRUE)' , lit , col_name ),
163
- startsWith = sprintf(' startsWith(get("%s"), %s)' , col_name , lit ),
164
- endsWith = sprintf(' endsWith(get("%s"), %s)' , col_name , lit ),
165
- regexp = sprintf(' grepl(%s, get("%s"))' , lit , col_name ),
166
- blank = sprintf(' is.na(get("%s")) | get("%s") == ""' , col_name , col_name ),
167
- notBlank = sprintf(' !is.na(get("%s")) & get("%s") != ""' , col_name , col_name ),
168
- inRange = {
169
- hi <- if (inherits(data [[col_name ]], " Date" ) && ! is.null(fd $ dateTo )) {
170
- sprintf(' as.Date("%s")' , fd $ dateTo )
173
+ if (is.null(cache_filtered_dt [[key ]]) || ! identical(filterModel , last_fm [[key ]])) {
174
+
175
+ dt1 <- cache_raw_dt [[key ]]
176
+
177
+ if (! is.null(filterModel ) && length(filterModel ) > 0 ) {
178
+ filter_strings <- lapply(names(filterModel ), function (fld ) {
179
+ fd <- filterModel [[fld ]]
180
+ col_name <- field_map [[fld ]]
181
+ if (! is.null(fd $ type ) && ! is.null(fd $ filter )) {
182
+ op <- fd $ type
183
+ raw <- if (fd $ filterType == " date" ) fd $ dateFrom else fd $ filter
184
+ lit <- if (inherits(dt1 [[col_name ]], " Date" )) {
185
+ sprintf(' as.Date("%s")' , raw )
186
+ } else if (is.numeric(dt1 [[col_name ]])) {
187
+ as.numeric(raw )
188
+ } else if (is.logical(dt1 [[col_name ]])) {
189
+ as.logical(raw )
171
190
} else {
172
- as.numeric( fd $ filterTo )
191
+ sprintf( ' "%s" ' , gsub( ' " ' , ' \\\" ' , raw ) )
173
192
}
174
- sprintf(' get("%s") >= %s & get("%s") <= %s' ,
175
- col_name , lit , col_name , hi )
176
- },
193
+ expr <- switch (op ,
194
+ equals = sprintf(" %s == %s" , col_name , lit ),
195
+ notEqual = sprintf(" %s != %s" , col_name , lit ),
196
+ greaterThan = sprintf(" %s > %s" , col_name , lit ),
197
+ greaterThanOrEqual = sprintf(" %s >= %s" , col_name , lit ),
198
+ lessThan = sprintf(" %s < %s" , col_name , lit ),
199
+ lessThanOrEqual = sprintf(" %s <= %s" , col_name , lit ),
200
+ contains = sprintf(" grepl(%s, %s, fixed=TRUE)" , lit , col_name ),
201
+ notContains = sprintf(" !grepl(%s, %s, fixed=TRUE)" , lit , col_name ),
202
+ startsWith = sprintf(" startsWith(%s, %s)" , col_name , lit ),
203
+ endsWith = sprintf(" endsWith(%s, %s)" , col_name , lit ),
204
+ regexp = sprintf(" grepl(%s, %s)" , lit , col_name ),
205
+ blank = sprintf(' is.na(%s) | %s == ""' , col_name , col_name ),
206
+ notBlank = sprintf(' !is.na(%s) & %s != ""' , col_name , col_name ),
207
+ inRange = {
208
+ hi <- if (inherits(dt1 [[col_name ]], " Date" ) && ! is.null(fd $ dateTo )) {
209
+ sprintf(' as.Date("%s")' , fd $ dateTo )
210
+ } else {
211
+ as.numeric(fd $ filterTo )
212
+ }
213
+ sprintf(" %s >= %s & %s <= %s" , col_name , lit , col_name , hi )
214
+ },
215
+ NULL
216
+ )
217
+ return (expr )
218
+ }
177
219
NULL
178
- )
179
- return (expr )
220
+ })
221
+ filter_strings <- Filter(Negate(is.null ), filter_strings )
222
+ if (length(filter_strings ) > 0 ) {
223
+ combined <- paste(filter_strings , collapse = " & " )
224
+ dt1 <- dt1 [eval(parse(text = combined ))]
225
+ }
180
226
}
181
- NULL
182
- })
183
-
184
- filter_strings <- Filter(Negate(is.null ), filter_strings )
185
- # combine with &&
186
- if (length(filter_strings ) > 0 ) {
187
- combined <- paste(filter_strings , collapse = " & " )
188
- data <- data [eval(parse(text = combined ))]
227
+ cache_filtered_dt [[key ]] <<- dt1
189
228
}
190
- }
191
229
192
- nFiltered <- nrow(data )
230
+ if (is.null(cache_dt [[key ]])
231
+ || ! identical(sortModel , last_sm [[key ]])
232
+ || ! identical(filterModel , last_fm [[key ]])) {
193
233
194
- if (! is.null(sortModel ) && length(sortModel ) > 0 ) {
234
+ dt2 <- cache_filtered_dt [[key ]]
235
+ if (! is.null(sortModel ) && length(sortModel ) > 0 ) {
236
+ cols <- vapply(sortModel , function (s ) field_map [[s $ colId ]], FUN.VALUE = " " )
237
+ ords <- vapply(sortModel , function (s ) if (s $ sort == " asc" ) 1L else - 1L , FUN.VALUE = integer(1 ))
238
+ sorted <- data.table :: copy(dt2 )
239
+ data.table :: setorderv(sorted , cols , order = ords )
240
+ } else {
241
+ sorted <- dt2
242
+ }
195
243
196
- cols <- vapply(sortModel , function (s ) field_map [[s $ colId ]], FUN.VALUE = " " )
197
- ords <- vapply(sortModel , function (s ) if (s $ sort == " asc" ) 1L else - 1L , FUN.VALUE = integer(1 ))
244
+ cache_dt [[key ]] <<- sorted
245
+ last_sm [[key ]] <<- sortModel
246
+ last_fm [[key ]] <<- filterModel
247
+ }
198
248
199
- data.table :: setorderv(data , cols , order = ords )
200
- }
249
+ # Fetch rows
250
+ out_dt <- cache_dt [[key ]]
251
+ totalUnfiltered <- cache_nrow [[key ]]
252
+ totalRows <- nrow(out_dt )
201
253
202
- if (is.null(end )) end <- nFiltered
203
- s <- as.integer(start ) + 1
204
- e <- min(nFiltered , as.integer(end ))
254
+ if (is.null(end )) end <- totalRows
255
+ s <- max( 1L , as.integer(start ) + 1 )
256
+ e <- min(totalRows , as.integer(end ))
205
257
206
- if (s > nFiltered || e < 1 || s > e ) {
207
- rows <- data [0 ]
208
- } else {
209
- rows <- data [s : e ][, " (row)" : = s : e ]
210
- }
258
+ if (s > totalRows || e < 1 || s > e ) {
259
+ rows <- out_dt [0 ]
260
+ } else {
261
+ rows <- out_dt [s : e ]
262
+ rows [, " (row)" : = seq.int(s , e )]
263
+ }
211
264
212
- names(rows ) <- fields
213
- class(rows ) <- " data.frame"
214
- attr(rows , " row.names" ) <- .set_row_names(nrow(rows ))
265
+ names(rows ) <- cache_fields [[key ]]
215
266
216
- columns <- .mapply(
217
- get_column_def ,
218
- list (.colnames , fields , rows ),
219
- NULL
220
- )
267
+ columns <- .mapply(
268
+ get_column_def ,
269
+ list (cache_colnames [[ key ]], cache_fields [[ key ]] , rows ),
270
+ NULL
271
+ )
221
272
222
- list (
223
- columns = columns ,
224
- rows = rows ,
225
- totalRows = nFiltered ,
226
- totalUnfiltered = .nrow
227
- )
228
- }
273
+ list (
274
+ columns = columns ,
275
+ rows = rows ,
276
+ totalRows = totalRows ,
277
+ totalUnfiltered = totalUnfiltered
278
+ )
279
+ }
280
+ })
229
281
230
282
if (use_webserver ) {
231
283
if (requireNamespace(" httpuv" , quietly = TRUE )) {
@@ -282,8 +334,24 @@ if (use_webserver) {
282
334
}
283
335
},
284
336
dataview_fetch_rows = function (varname , start , end , sortModel , filterModel , ... ) {
285
- obj <- get(varname , envir = .GlobalEnv )
286
- out <- dataview_table(obj , start , end , sortModel , filterModel )
337
+
338
+ if (! exists(" .dataview_first_map" , envir = .GlobalEnv , inherits = FALSE )) {
339
+ assign(" .dataview_first_map" , new.env(parent = emptyenv()), envir = .GlobalEnv )
340
+ }
341
+ fm_env <- get(" .dataview_first_map" , envir = .GlobalEnv )
342
+
343
+ obj <- if (exists(varname , envir = .GlobalEnv )) {
344
+ get(varname , envir = .GlobalEnv )
345
+ } else {
346
+ eval(parse(text = varname ), envir = .GlobalEnv )
347
+ }
348
+
349
+ attr(obj , " _dvkey" ) <- varname
350
+
351
+ is_first <- is.null(fm_env [[varname ]])
352
+ fm_env [[varname ]] <- TRUE
353
+
354
+ out <- dataview_table(obj , start , end , sortModel , filterModel , force = is_first )
287
355
out $ columns <- NULL
288
356
return (out )
289
357
}
@@ -712,14 +780,18 @@ if (show_view) {
712
780
}
713
781
}
714
782
if (is.data.frame(x ) || is.matrix(x )) {
783
+ expr_text <- deparse1(substitute(x ))
715
784
x <- as_truncated_data(x )
716
- # Get initial chunk of data (first 100 rows)
717
- meta <- dataview_table(x , start = 0 , end = 1 )
718
- meta $ rows <- list ()
785
+ if (exists(" .dataview_first_map" , envir = .GlobalEnv , inherits = FALSE )) {
786
+ fm_env <- get(" .dataview_first_map" , envir = .GlobalEnv )
787
+ fm_env [[title ]] <- NULL
788
+ }
789
+ meta <- dataview_table(x , start = 0 , end = 0 , metadata_only = TRUE , force = TRUE )
719
790
file <- tempfile(tmpdir = tempdir , fileext = " .json" )
720
791
jsonlite :: write_json(meta , file , na = " string" , null = " null" , auto_unbox = TRUE , force = TRUE )
721
792
request(" dataview" , source = " table" , type = " json" ,
722
- title = title , file = file , viewer = viewer , uuid = uuid , dataview_uuid = dataview_uuid
793
+ title = title , file = file , viewer = viewer ,
794
+ uuid = uuid , dataview_uuid = dataview_uuid , expr = expr_text
723
795
)
724
796
} else if (is.list(x )) {
725
797
tryCatch({
0 commit comments