Searching for a range of dates

Author: Dave Cassel  |  Category: Software Development

I learned something interesting today, so I thought I’d share. A colleague of mine wanted to figure out how to express a range of dates as a query string for an Application Builder app. These applications use the Search API, so a lot of the search magic is in the options that get passed into the search:search() calls. It just so happens that my pins collection site tracks when I got each pin, so I was able to explore a little pretty easily. I have a range index set up on the acquired-date element as an xs:date. I also have a constraint set up on that element to show a facet. It happens to be a bucketed facet, the configuration for which looks like this:

<constraint name="acquired-date">
    <range type="xs:date" facet="true">
        <computed-bucket lt="P0Y" ge="-P1Y" anchor="start-of-year" name="lastyear">Last Year</computed-bucket>
        <computed-bucket lt="-P1Y" ge="-P100Y" anchor="start-of-year" name="older">Older</computed-bucket>
        <computed-bucket lt="P1M" ge="P0M" anchor="start-of-month" name="thismonth">This Month</computed-bucket>
        <computed-bucket lt="P1Y" ge="P0Y" anchor="start-of-year" name="thisyear">This Year</computed-bucket>
        <facet-option>frequency-order</facet-option>
        <facet-option>descending</facet-option>
        <facet-option>limit=10</facet-option>
        <element ns="http://davidcassel.net/coll" name="acquired-date"/>
    </range>
    <annotation>
        <proj:front-page xmlns:proj="http://marklogic.com/appservices/project">true</proj:front-page>
        <proj:side-bar xmlns:proj="http://marklogic.com/appservices/project">true</proj:side-bar>
    </annotation>
</constraint>

The code is in /application/lib/config.xqy. This sets up a nice facet that groups my pins based on when I got them. I like that. Now let’s take a look at another part of the $OPTIONS-STANDARD setup in config.xqy:

<grammar>
    <!-- ... -->
    <joiner strength="50" apply="constraint" compare="LT" tokenize="word">LT</joiner>
    <joiner strength="50" apply="constraint" compare="GT" tokenize="word">GT</joiner>
</grammar>

I’ve cut out several parts of the grammar element to focus on these handy less-than and greater-than joiners. Based on them, I can make a query that only returns documents where a constraint’s value is greater than (or less than) a particular value. For instance, if I had a constraint set up based on current value of my pins, I could look for pins that have a value of at least $10 with “current-value GT 10”. Pretty cool. So at first I thought that would solve the date range query. If I want the pins I got between 1990 and 2000, I could do this: “acquired-date GT 1990-01-01 AND acquired-date LT 2000-12-31”.

I expected that to work, but it didn’t (although I found that you could do “acquired-date LT thisyear”). I tried some other experiments that convinced me the GT grammar itself worked, so I figured there must be something about the particular constraint. On a hunch, I set up a new constraint without the buckets:

<constraint name="adate">
    <range type="xs:date" facet="false">
        <facet-option>frequency-order</facet-option>
        <facet-option>descending</facet-option>
        <facet-option>limit=10</facet-option>
        <element ns="http://davidcassel.net/coll" name="acquired-date"/>
    </range>
    <annotation>
        <proj:front-page xmlns:proj="http://marklogic.com/appservices/project">true</proj:front-page>
        <proj:side-bar xmlns:proj="http://marklogic.com/appservices/project">true</proj:side-bar>
    </annotation>
</constraint>

No buckets, and now when I tried this query: “adate GT 1990-01-01 AND adate LT 2000-12-31”, it worked as expected. While I haven’t yet looked into the details, it looks like you can query against bucket values (like thismonth and lastyear) or you can query against the actual values (by equalities or inequalities). I haven’t found a way to mix them yet, but it’s good to know that you can do both.

 

Tags: ,

Leave a Reply