How to search content using a date range on Plone

774 Views Asked by At

Plone gives you the possibility of filtering search results using a date range. Normal date range is set up by using only the minimum date so the range is between today and some date on the past.

I need to add the option of setting a date range with starting and ending dates.

I was thinking on adding an extra option that, when selected, will show a couple of date pickers, but I don't know how to set the created.query:record:list:date variable to use this 2 values.

I have tried passing a list on the query variable but then I get:

Error Value: Invalid DateTime "[DateTime('2000/07/14 00:00:00 GMT-3'), DateTime('2014/07/14 00:00:00 GMT-3')]"

Has anybody else has found with a requirement like this before? How did you solved it? Should this be ported to the core?

2

There are 2 best solutions below

0
hvelarde On BEST ANSWER

I spent some time investigating this and I have a working solution.

First we need to understand how Zope and ZPT work; when you name a form element with something like created.query:record:list:date you are telling Zope to do some parameter conversions:

  • record, converts the variable to a record attribute
  • list, converts the variable to a Python list
  • date, converts a string to a DateTime object

For more information on this you can take a look at Advanced Zope Scripting.

So, the parameter is then automatically converted to a dictionary record like this in Python:

'created': {'query': [DateTime('2014/07/09 00:00:00 GMT-3')], 'range': 'min'}

My mistake was assuming that I had to pass all list elements on one request variable.

The code I added at the end of the form now looks like this:

<input type="radio"
       id="query-date-range"
       name="created.query:record:list:date:ignore_empty"
       tal:attributes="value python:'';
                       checked python:checked=='' and 'checked' or '';"
       />
<label for="query-date-range" i18n:translate="time_range">Time range</label>
<input type="text"
       id="range-start"
       name="created.query:record:list:date:ignore_empty">
<input type="text"
       id="range-end"
       name="created.query:record:list:date:ignore_empty">
<script>
var today = new Date();
var yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
var start = yesterday.toLocaleDateString().slice(0, 10);
var end = today.toLocaleDateString().slice(0, 10);
$(document).ready(function() {
    $("#range-start").datepicker();
    $("#range-end").datepicker();
    $('#range-start').hide();
    $('#range-end').hide();
});

$('input:radio').change(function() {
    var option = $('input:radio:checked', '#searchform').val();
    if (option === '') {
        $('#range-start').val(start);
        $('#range-start').show();
        $('#range-end').val(end);
        $('#range-end').show();
        $('#range').val('min:max');
    }
    else {
        $('#range-start').val('');
        $('#range-start').hide();
        $('#range-end').val('');
        $('#range-end').hide();
        $('#range').val('min');
    };
});
</script>

Take special attention to the following:

  • the start and end dates are input elements associated with a jQueryUI datepicker widget.

  • both input elements share the same name (created.query:record:list:date:ignore_empty) as the other and here is where the Zope magic takes place: the values of all input elements with the same name are converted on a unique list record like this:

'created': {'query': [DateTime('2014/07/15 00:00:00 GMT-3'), DateTime('2014/07/16 00:00:00 GMT-3')], 'range': 'min:max'}
  • we set an additional parameter converter (ignore_empty), which just avoid adding to the list the elements that have no value set (I added this converter to the other input elements too).

  • when we click on the query-date-range element we have to initialize the date selectors; when we click on any of the other elements we need to clear these values to avoid adding them to the list.

  • we also change the value of the range element according to the query type as mentioned before by @sdupton.

The JavaScript part of the code could be enhanced for sure; my skills are really limited.

2
sdupton On

You likely want to pass a query dict for your index name -- with a range parameter. Supposing your index was 'modified' and you wanted a range of minimum and maximum, something like:

>>> from DateTime import DateTime
>>> from Products.CMFCore.utils import getToolByName
>>> catalog = getToolByName(site)
>>> startDT = DateTime(2010,12,1)
>>> endDT = DateTime(2013,12,1)
>>> query = {
...     'modified': {
...         query: (startDT, endDT),
...         range: 'min:max',
...         }
...     }
... 
>>> result = catalog.unrestrictedSearchResults(query)  # or .search()

Something similar is documented here: http://docs.plone.org/develop/plone/searching_and_indexing/query.html#querying-by-date