skip to Main Content

For demonstration purposes, I have implemented an example datatable in shiny, where one can search the whole datatable (by using the search field in the top-right corner) and/or within a specific column (by using the search fields above the columns).
The smart search (e.g. searching the whole datatable or the column car for "Ma W" shows "Mazda RX4 Wag") works fine the search fields, when the datatable is processed on the client-side (SERVER = FALSE).
However, if the datatable is processed on the server-side (SERVER = TRUE), the smart search works only for the whole datatable search, but stops working for the column search.
Is there a way to enable smart search also for the columns with server-side processing?
Thank you in advance.

Example datatable

library(DT)
library(shiny)

ui = fluidPage(
   fluidRow(
      column(width = 12,
             DTOutput("dtable")
      )
   )
)

server = function(input, output, session) {
   data = data.frame(
      car = c("Mazda", "Mazda RX4", "Mazda RX4 Wag", "Ford", "Mercedes"),
      pet = c("dog", "dog", "cat", "cat", "cat")
   )

   output$dtable = renderDT({
      js = c(
         "function(settings) {",
         "  var instance = settings.oInstance;",
         "  var table = instance.api();",
         "  var $inputs = instance.parent().find('.form-group input');",
         "  $inputs.off('keyup search input').on('keyup', function() {",
         "    var value = $(this).val();",
         "    if(value !== '') {",
         "      var index = 1 + $inputs.index(this);",
         "      var column = table.column(index);",
         "      column.search(value, false, true, true).draw();",  # -> regex, smart, caseInsensitive
         "    }",
         "  });",
         "}"
      )
      datatable(
         data, filter = "top",
         options = list(
            dom = "ft",
            columnDefs = list(
               list(targets = "_all", className = "dt-center")
            ),
            search = list(regex = FALSE, smart = TRUE, caseInsensitive = TRUE),  # -> is redundant due to the following up js = c(...) part
            initComplete = JS(js)
         )
      )
   }, server = FALSE)  # -> switch for client-side (server=FALSE) or server-side (SERVER=TRUE) processing
}

shinyApp(ui = ui, server = server)

2

Answers


  1. Chosen as BEST ANSWER

    Since the smart-search logic is still working for the whole datatable search while processing the data on the server-side (SERVER=TRUE), I had to adapt the column search in order to receive an OR-logic (smart-search logic) the following way:

    library(DT)
    library(shiny)
    
    ui = fluidPage(
       fluidRow(
          column(width = 12,
                 DTOutput("dtable")
          )
       )
    )
    
    server = function(input, output, session) {
       data = data.frame(
          car = c("Mazda", "Mazda RX4", "Mazda RX4 Wag", "Ford", "Mercedes"),
          pet = c("dog", "dog", "cat", "cat", "cat")
       )
       
       output$dtable = renderDT({
          js = c("
             function(settings) {
                var instance = settings.oInstance;
                var table = instance.api();
                var $inputs = instance.parent().find('.form-group input');
                $inputs.off('keyup search input').on('keyup', function() {
                   var value = $(this).val().trim();  // trim leading and trailing spaces
                   var index = 1 + $inputs.index(this);
                   var column = table.column(index);
                   if (value === '') {  // clear the search and redraw the table
                      column.search('').draw();
                      return;
                   }
                   value = value.replace(/\s+/g, '|');  // replace spaces with OR operators
                   column.search(value, true, false, true).draw();  // -> regex, smart, caseInsensitive
                   setTimeout(function() {
                      column.nodes().to$().highlight(value.split('|')); // highlight search results
                   }, 100);
                });
             }
          ")
          datatable(
             data, filter = "top",
             options = list(
                dom = "ft",
                columnDefs = list(
                   list(targets = "_all", className = "dt-center")
                ),
                fixedHeader = FALSE,
                searchHighlight = TRUE,
                search = list(regex = FALSE, smart = TRUE, caseInsensitive = TRUE),
                searchCols = list(list(search = "", regex = TRUE, smart = FALSE, caseInsensitive = TRUE, escapeRegex = TRUE),  # car
                                  list(search = "", regex = TRUE, smart = FALSE, caseInsensitive = TRUE, escapeRegex = TRUE)  # pet
                ),
                initComplete = JS(js)
             )
          )
       }, server = TRUE)  # -> switch for client-side (server=FALSE) or server-side (SERVER=TRUE) processing
    }
    
    shinyApp(ui = ui, server = server)
    

  2. Smart search on individual columns is currently not implemented in DT for the server mode. However, you can add the functionality by implementing your own search function by following these two steps:

    1. Define your own search function for smart search:
    smartColumnSearch <-
      function (col, search_string, options = list()) {
        `%||%` <- DT:::`%||%`
        n = length(col)
        if (length(v <- search_string) > 0) {
          if (options$smart %||% TRUE) {
            v = unlist(strsplit(gsub("^\s+|\s+$", "", v),
                                "\s+"))
          }
        }
        
        if (length(v) == 0)
          v = ""
        
        m = if ((nv <- length(v)) > 1)
          array(FALSE, c(length(col), nv))
        else
          logical(n)
        
        if (!identical(v, "")) {
          for (k in seq_len(nv)) {
            i0 = DT:::grep2(
              v[k],
              as.character(col[, drop = TRUE]),
              fixed = !(options$regex %||% FALSE),
              ignore.case = options$caseInsensitive %||%
                TRUE
            )
            if (nv > 1)
              m[i0, k] = TRUE
            else
              m[i0] = TRUE
          }
          which(if (nv > 1)
            apply(m, 1, function(z)
              all(z > 0))
            else
              m)
        }
        else
          seq_len(n)
      }
    
    1. Searching is defined inside the internal function DT:::dataTablesFilter. This function has to be modified. Call
    fixInNamespace("dataTablesFilter", "DT")
    

    Then a window will open where you change the part (currently approximately around line 45):

    column_opts = list(regex = col[["search"]][["regex"]] != 
                         "false", caseInsensitive = global_opts$caseInsensitive)
    dj = data[i, j]
    i = i[doColumnSearch(dj, k, options = column_opts)]
    

    to this one:

    column_opts = list(regex = col[["search"]][["regex"]] != 
                         "false", caseInsensitive = global_opts$caseInsensitive,
                       smart = global_opts$smart)
    dj = data[i, j]
    i = i[smartColumnSearch(dj, k, options = column_opts)]
    

    Save this and it should work while having server = TRUE. Then your custom function is called when searching and the smart search is enabled via smart = TRUE what you already defined in your code. Also notice that you have to disable your js and that this approach also should work for server = FALSE.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search