SearchDelay for Server-side issue

SearchDelay for Server-side issue

xfloydxfloyd Posts: 35Questions: 12Answers: 1

Is that confirmed issue, or is it in my implementation only that the filter input fires twice. Once immediately on the first letter typed, and second time after the set delay time. I see that in few of my datatables implementations for version 1.10.10

«1

Answers

  • allanallan Posts: 61,439Questions: 1Answers: 10,053 Site admin

    That immediate trigger is a mistake that I need to look into. I'm actually thinking of changing searchDelay in future versions to only trigger after a set amount of time rather than at a given frequency...

    Allan

  • VyacheslavVyacheslav Posts: 70Questions: 27Answers: 0

    Yeeees, I'm waiting too ... in my case (server-side mode) filter input fires every key-down(

  • gcurrie333gcurrie333 Posts: 6Questions: 1Answers: 0

    Not to pile on too obviously, but I would like this too. I have a server-side app where Search is unusable because it hammers the server with every keystroke.

  • loloskiloloski Posts: 46Questions: 5Answers: 1

    I don't know if this is the right answer, but this his how I do it for the search for the time being so that the server will not be hammered during keystroke

    $("div.dataTables_filter input").unbind();
    $("div.dataTables_filter input").keyup( function (e) {
    if (e.keyCode == 13) {
    table.search( this.value ).draw();
    }
    });

  • allanallan Posts: 61,439Questions: 1Answers: 10,053 Site admin

    Thanks for the bump - I really must make some time to look at this. I will try to do so as soon as possible (dependent on everything else that is going on).

    The next major version of DataTables will likely replace the throttle with a debounce.

    Allan

  • jasonperronejasonperrone Posts: 9Questions: 0Answers: 0

    I'm going to have to go ahead on pile on this too, so you guys know that a decent number of folks are affected. I am basically going to have to not allow my users to live filter on this one server side table I have in my app, which stinks, because that functionality exists on all the other tables, which are client side. But if you type a string like "banana" I get like 9 server calls, and it's a pretty substantial query.

  • allanallan Posts: 61,439Questions: 1Answers: 10,053 Site admin

    You shouldn't be getting a call for every letter - that's very wrong if that is happening and I would like a link to a test case showing that issue. The issue this thread discusses is that when the throttle starts it immediately triggers a request, rather than waiting until the end of the cycle. After that it should then wait for the period defined.

    Allan

  • asciiascii Posts: 1Questions: 0Answers: 0

    Is there a workaround now to prevent the immediate trigger and only trigger after a set amount of time rather than at a given frequency as you noted above? I'm using DataTables 1.10.13

    ~/scott

  • allanallan Posts: 61,439Questions: 1Answers: 10,053 Site admin

    No - not yet I'm afraid. That change will be made in the next major version of DataTables.

    Allan

  • btreebtree Posts: 99Questions: 14Answers: 11

    Hi allan,

    want to push the thread and is there a way to disable Autosearch?

    Cheers
    Hannes

  • allanallan Posts: 61,439Questions: 1Answers: 10,053 Site admin

    Hi Hannes,

    I'm afraid this isn't an issue that I've looked into yet. I've not done the fork for the next major version yet and it will likely be a few months before I do.

    By auto search do you mean the search on key press? If so, then no, there is no built in way to disable that on the provided search box. Instead what you would do is have your own search box and use search() to trigger the search whenever it is that you want to do the search.

    Allan

  • btreebtree Posts: 99Questions: 14Answers: 11

    Hi Allan,

    no worries just wanted to push it. ;)

    Yes, because one Table is quite big and I only want to start searching when the user hits the enter key. I will do your workaround thanks!

    Cheers
    Hannes

  • wyattbikerwyattbiker Posts: 25Questions: 14Answers: 0
    edited April 2017

    piggybacking on loloski's idea, i used setTimout(). Works as good as original.

    eg.

    var input_filter_value;
    var input_filter_timeout=null;
    usertable=$('#users').DataTable( {
        searchDelay: 1500;
    }
    
    ....
    
        $("div.dataTables_filter input").unbind();
        $("div.dataTables_filter input").keyup( function (e) {
            input_filter_value=this.value;
            clearTimeout(input_filter_timeout);
            input_filter_timeout=setTimeout(function(){
                usertable.search( input_filter_value ).draw();
            }, usertable.context[0].searchDelay);
    
            // if (e.keyCode == 13) {
            //  usertable.search( this.value ).draw();
            // }
        });
    
  • Dennis14eDennis14e Posts: 13Questions: 2Answers: 0

    @allan Is there an update to this issue?
    As workaround I have slightly modified the version of @wyattbiker to use this in the column search:

    var input_filter_timeout;
    $('#table_dt thead input').on('keyup change', function() {
      that = this
      clearTimeout(input_filter_timeout);
      input_filter_timeout = setTimeout(function() {
        table
          .column($(that).parent().index() + ':visible')
          .search(that.value)
          .draw();
      }, table.context[0].searchDelay);
    });
    
  • allanallan Posts: 61,439Questions: 1Answers: 10,053 Site admin

    I'm sorry - not yet. i'll update the thread when I do.

    Allan

  • jimhapjimhap Posts: 4Questions: 0Answers: 0

    Having same problem with too many unnecessary queries being executed against large dataset. I REALLY like loloski's solution, ie forcing user to submit their search via enter key. I took it a step further and put a checkmark icon in the search box which users can tap or click to submit search.

  • IvanHalenIvanHalen Posts: 2Questions: 1Answers: 0

    I came here from this thread, that unfortunately is closed... I guess the current thread is a variation of the same issue, please any fix?

  • kthorngrenkthorngren Posts: 20,141Questions: 26Answers: 4,736
  • rduncecbrduncecb Posts: 125Questions: 2Answers: 28

    Inspired by @bindred's post and using the debounce function from https://davidwalsh.name/javascript-debounce-function use this to debounce the search input of a datatable.

    Calling this with something like debounceSearch('#example') will cause the ajax call to be triggered only if there is is a pause between keyup events of 300ms. While the user is typing it will wait 300ms after they have stopped (unless they type slowly with > 300ms gap between keypresses) to update the table. I've found it can reduce the number of ajax calls a bit over the searchDelay option.

    function debounceSearch(tableId) {
        var $searchBox = $(tableId + "_filter input[type='search']");
        $searchBox.off();
    
        var searchDebouncedFn = debounce(function() {
            $(tableId).DataTable().search($searchBox.val()).draw();
        }, 300);
    
        $searchBox.on("keyup", searchDebouncedFn);
    }
    
    /* from https://davidwalsh.name/javascript-debounce-function */
    function debounce(func, wait, immediate) {
        var timeout;
        return function() {
            var context = this, args = arguments;
            var later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };
    
  • esteban.olmesteban.olm Posts: 4Questions: 2Answers: 0

    Sorry, I have the same issue.

    With searchDelay=2000, first key does a call to the server, then it waits 2 seconds.

    So, always having minimum two request to the server. Bad !!!

    It shouldn't call the server on first key !! IT has to wait searchDelay milliscons to call for the fisrt time.

    PLEEEEEASSSSEEEE !!, TWO YEARS FOR THIS BUG !!

  • kthorngrenkthorngren Posts: 20,141Questions: 26Answers: 4,736

    Have you tried one of the work arounds in this thread?

    Kevin

  • gboussingboussin Posts: 13Questions: 4Answers: 0
    edited January 2019

    Dear all,

    I am posting on this old subject as I have the same problem of performance with searchDelay. It seems to me the best neutral patch is the one recalled by rduncecb in March, which works great for me.
    However to keep the exact events used by search in datatables.js, I use .on("keyup.DT search.DT input.DT paste.DT cut.DT", ...) instead of just keyup.
    NB: the patch needs to be used in the initComplete: function( settings, json ) { ... } of Datatables, as the input is not created right away with the call of Datatables.

    Allan, I believe integrating this patch would solve a problem for all users with big database. There are other interesting solutions like the ones on https://stackoverflow.com/questions/5548893/jquery-datatables-delay-search-until-3-characters-been-typed-or-a-button-clicke/5646667 but they are more specific and thus may still be used on a custom basis.

    => Could you please change your standard "keyup.DT search.DT input.DT paste.DT cut.DT" in _fnFeatureHtmlFilter to use the very stable debounce function?

  • lukegonglukegong Posts: 1Questions: 0Answers: 0

    After doing some research on https://stackoverflow.com/questions/5548893/jquery-datatables-delay-search-until-3-characters-been-typed-or-a-button-clicke/5646667

    I think we need to customize this in initComplete method. It will call the server every 1 second (you can change the time anyway).

    initComplete: function() {
                        var api = this.api();
                        var searchWait = 0;
                        var searchWaitInterval;
                        // Grab the datatables input box and alter how it is bound to events
                        $(".dataTables_filter input")
                        .unbind() // Unbind previous default bindings
                        .bind("input", function(e) { // Bind our desired behavior
                            var item = $(this);
                            searchWait = 0;
                            if(!searchWaitInterval) searchWaitInterval = setInterval(function(){
                                searchTerm = $(item).val();
                                // if(searchTerm.length >= 3 || e.keyCode == 13) {
                                    clearInterval(searchWaitInterval);
                                    searchWaitInterval = '';
                                    // Call the API search function
                                    api.search(searchTerm).draw();
                                    searchWait = 0;
                                // }
                                searchWait++;
                            },1000);                        
                            return;
                        });
                    },
    
  • redsunsetredsunset Posts: 44Questions: 15Answers: 0

    @lukegong
    fantastic solution for this problem! works very smooth. thank you for your share!

  • valdocr94valdocr94 Posts: 1Questions: 0Answers: 0

    Excellent solution helped me solve my problem. Thank you very much @lukegong

  • odumetzodumetz Posts: 1Questions: 0Answers: 0
    edited July 2019

    This is a jquery based approach, using BindWithDelay (https://github.com/bgrins/bindWithDelay) I find the best approach is to push users to use the enter key... yet if some users cannot figure that out, still fire the search.

    Here the change event (hitting "enter", losing focus, etc.) will launch the search and save the search string. The input event covers keyup, pasting, etc.

    var searchTerm = "";
    $(".dataTables_filter input")
        .unbind() // Unbind previous default bindings
        .bind("change", function (e) { // Search on change event
            searchTerm = $(this).val();
            oTable.search(searchTerm).draw();
        })
        .bindWithDelay("input", function (e) {
            if ($(this).val() != searchTerm) $(this).trigger('change'); //only trigger if text is different than last search
        },2000);
    

    The good thing here is that when you hit enter (or encounter the change event), the delayed function will not fire.

  • pjdarchpjdarch Posts: 1Questions: 0Answers: 0
    edited October 2019

    This should solve everyone's problem. The code below only executes the server side search once a user has stopped typing and 1 second has transpired. Courtesy goes to https://stackoverflow.com/a/1909508 for the delay function. Just as an FYI, I elected on using the older delay function in my example below, as the new function from 2019-05-16 does not support Internet Explorer.

    $('.dataTables_filter input')
        .unbind() // Unbind previous default bindings
        .bind('input', (delay(function (e) { // Bind our desired behavior
            oTable.search($(this).val()).draw();
            return;
        }, 1000))); // Set delay in milliseconds
    
    function delay(callback, ms) {
        var timer = 0;
        return function () {
            var context = this, args = arguments;
            clearTimeout(timer);
            timer = setTimeout(function () {
                callback.apply(context, args);
            }, ms || 0);
        };
    }
    
  • therultherul Posts: 6Questions: 1Answers: 0

    As much as I realize that multiple searches cause backend problems,
    I have found an easier answer, and it does it much cleaner.
    I return the search item from the server in the Echo,
    then I check the Echo against what is currently in the Search box, so that any returns that are not current are ignored.

    Code did not want to play... so I did not post it.

  • therultherul Posts: 6Questions: 1Answers: 0

    `'fnServerData': function (ssource, aoData, fnCallback) {

                $('.modTable').prop('disabled', true);
    
    
                var outbound = {};
                for (var i = 0; i < aoData.length; i++) {
                    if (aoData[i].name === 'draw') {
                        outbound.sEcho = aoData[i].value;
                    }
                    if (aoData[i].name === 'columns') {
                        outbound.iColumns = aoData[i].value;
                    }
                    if (aoData[i].name === 'sColumns') {
                        outbound.sColumns = aoData[i].value;
                    }
                    if (aoData[i].name === 'start') {
                        outbound.DisplayStart = aoData[i].value;
                    }
                    if (aoData[i].name === 'length') {
                        outbound.DisplayLength = aoData[i].value;
                    }
                    if (aoData[i].name === 'search') {
                        outbound.Search = aoData[i].value.value;
                        outbound.sEcho = aoData[i].value.value;
                        if (searchTerm === aoData[i].value.value) {
                            //exit
                            return;
                        }
                        else {
                            if (searchTerm === undefined) {
                                searchTerm = aoData[i].value.value;
    
                            }
                            else if (aoData[i].value.value.length <3) {
                                //input_filter_timeout = setTimeout(function () {
                                //    $('#tblItemList').DataTable().search(searchTerm).draw();
                                //    $(".dataTables_processing").hide();
                                //}, 500);
                                //searchTerm = aoData[i].value.value;
                                return;
                            }
                            searchTerm = aoData[i].value.value;
                        }
                    }
                    if (aoData[i].name === 'order') {
                        outbound.OrderBy = aoData[i].value[0].column;
                        outbound.OrderDir = aoData[i].value[0].dir;
                    }
                }
                $('.modItemRefresh').removeClass('hide');
                outbound.CustNo = sessionStorage.custNo;
                if (outbound.OrderBy === undefined) {
                    outbound.OrderBy = 1;
                    outbound.OrderDir = 'asc';
                }
                outbound.OrderGuide =
                    $('#' + ddlOrderGuides.id + ' option:selected').val() === undefined ?
                        "" : $('#' + ddlOrderGuides.id + ' option:selected').val();
                if (outbound.OrderGuide === '') {
                    outbound.OrderGuide = 'All Items';
                }
                outbound.Guidename = outbound.OrderGuide;
                var args = JSON.stringify({
                    args: outbound
                });
    
    
                    dataService.productMapping(args, 'GetAllItems')
                        .done(function (data) {
                            if ($('#tblItemList_filter input')[0] === undefined
                                || data.d[0].Echo === $('#tblItemList_filter input')[0].value 
                                || data.d.length ===0) {
                                var newData = [];
                                for (var i = 0; i < data.d.length; i++) {
                                    newData.push(data.d[i]);
                                }
                                var retData = {};
                                if (newData.length === 0) {
                                    var retData0 = {
                                        "draw": 1,
                                        "recordsTotal": 0,
                                        "recordsFiltered": 0,
                                        "sEcho": ''
                                    };
                                    retData = retData0;
                                }
                                else {
                                    var retData1 = {
                                        "draw": 1,
                                        "recordsTotal": newData[0].TotalRecords,
                                        "recordsFiltered": newData[0].FilteredRecords,
                                        "sEcho": newData[0].Echo
                                    };
                                    retData = retData1;
                                }
                                retData.data = newData;
    
                                fnCallback(retData);
                            }
                        })
                        .fail(function (jqXHR, txtStatus, errorTrhown) {
                            dataService.fail(jqXHR, txtStatus, errorTrhown);
                        })
                        .always(function () {
                            $('.modTable').prop('disabled', false);
                            $('#tmpRefresh').remove();
                            $('.modItemRefresh').addClass('hide');
                        });                
            }`
    
  • stevevancestevevance Posts: 58Questions: 6Answers: 1
    edited January 2020

    I took pjdarch's function and turned it into a feature plugin.

    $.fn.dataTable.Debounce = function ( table, options ) {
        var tableId = table.settings()[0].sTableId;
        $('.dataTables_filter input[aria-controls="' + tableId + '"]') // select the correct input field
            .unbind() // Unbind previous default bindings
            .bind('input', (delay(function (e) { // Bind our desired behavior
                table.search($(this).val()).draw();
                return;
            }, 1000))); // Set delay in milliseconds
    }
    
    function delay(callback, ms) {
        var timer = 0;
        return function () {
            var context = this, args = arguments;
            clearTimeout(timer);
            timer = setTimeout(function () {
                callback.apply(context, args);
            }, ms || 0);
        };
    }
    

    Then, after you've instantiated your DataTable, you need to create a new instance of the feature on a given table:

    var table = $("#table_selector").DataTable();
    var debounce = new $.fn.dataTable.Debounce(table);
    
Sign In or Register to comment.