Posted on

Natural-language date queries

I just wanted to be able to type in a date on the search and for the search to come up with all the blog posts for that date.

This is one of those tasks that sounds really simple, but is actually complicated. However, this question piqued my interest and turns out it isn’t that hard.

add_filter( 'relevanssi_modify_wp_query', 'rlv_date_query' );
function rlv_date_query( $query ) {
	$time = strtotime( $query->query_vars['s'] );
	if ( $time ) {
		$year     = date( 'Y', $time );
		$use_year = strpos( $query->query_vars['s'], $year ) !== false ? true : false;
		$month    = date( 'm', $time );
		$day      = date( 'd', $time );
		$date_query = array(
			'month' => $month,
			'day'   => $day,
		);
		if ( $use_year ) {
			$date_query['year'] = $year;
		}
		$query->set( 'date_query', $date_query );
		$query->query_vars['s'] = '';
	}
	return $query;
}

The solution is simple. The search query ($query->query_vars['s']) is fed to PHP’s strtotime() function, which converts an English-language string to a timestamp. If the search query converts to a timestamp, the timestamp is converted into a date and used for a date query.

The function also checks if the year appears in the query. If it doesn’t, the year isn’t used. Yearless dates are converted into current year timestamps (“16 August” matches August 16th, 2019), but if the year isn’t mentioned in the original query, it’s discarded, so the results include all years. The search query is then swiped clean.

This only works when there’s nothing else but the date in the query. This supports many different formats, all of these work at least:

– 16 August or 16 August 2019
– August 16th or August 16th 2019
– 16.8.2019
– 8/16/2019

(As it happens, this isn’t Relevanssi-specific. If you’re not using Relevanssi, you just need to use pre_get_posts instead of relevanssi_modify_wp_query.)

17 comments Natural-language date queries

  1. Is there a way to do a OR Date ? Like I want to search the content for that date OR for the published date… Since I have field with date in the content, it block each others.

      1. Wow – that was quick! It works like a charm, although I had to paste the code into my functions before the ‘Natural language date queries’ function, as I understand it – the year query has to look for the four digits first and then reset the query?

        Is it possible to have the results run with the oldest date entires first?

        1. If you are running both of these functions together, you probably want the natural language date query to run first – if the whole search string is a date, process it that way instead of looking just for the year. The natural language date query will empty the search string afterwards, so the year query does nothing after that.

          So, change add_filter( 'relevanssi_modify_wp_query', 'rlv_date_query' ); to add_filter( 'relevanssi_modify_wp_query', 'rlv_date_query', 9);. This will make the rlv_date_query() function always run first and you’ll get more consistent behaviour that doesn’t depend of the order of the functions in the file – nothing should depend on a random factor like that, reorganising your functions.php should never change your site functionality!

          1. I’m so sorry – am I missing something – this is what I have and it’s not working

            /* Search / Relevanssi / Natural-language date queries
            ————————————————————————————— */
            add_filter( ‘relevanssi_modify_wp_query’, ‘rlv_date_query’, 9 );
            function rlv_date_query( $query ) {
            $time = strtotime( $query->query_vars[‘s’] );
            if ( $time ) {
            $year = date( ‘Y’, $time );
            $use_year = strpos( $query->query_vars[‘s’], $year ) !== false ? true : false;
            $month = date( ‘m’, $time );
            $day = date( ‘d’, $time );
            $date_query = array(
            ‘month’ => $month,
            ‘day’ => $day,
            );
            if ( $use_year ) {
            $date_query[‘year’] = $year;
            }
            $query->set( ‘date_query’, $date_query );
            $query->query_vars[‘s’] = ”;
            }
            return $query;
            }

            /* Search / Relevanssi / Year only filter
            ————————————————————————————— */
            add_filter( ‘relevanssi_modify_wp_query’, function( $query ) {
            $m = preg_match( ‘/\b\d{4}\b/’, $query->query_vars[‘s’], $matches );
            if ( 1 === $m ) {
            $year = $matches[0];
            if ( $year > 1900 && $year query_vars[‘s’];

            $query->query_vars[‘year’] = $year;
            $query->query_vars[‘s’] = str_replace( $year, ”, $query->query_vars[‘s’] );
            }
            }
            return $query;
            } );

            add_filter( ‘posts_pre_query’, function( $a, $b ) {
            global $wp_query, $relevanssi_original_query;
            $wp_query->query_vars[‘s’] = $relevanssi_original_query;
            return $a;
            }, 100, 2 );

          2. Oh – that’s bad! Apologies… I’ve corrected the code but it still doesn’t see to be working as expected. I could give you a login?

            /* Search / Relevanssi / Natural-language date queries
            ————————————————————————————— */
            add_filter( ‘relevanssi_modify_wp_query’, ‘rlv_date_query’, 9);
            function rlv_date_query( $query ) {
            $time = strtotime( $query->query_vars[‘s’] );
            if ( $time ) {
            $year = date( ‘Y’, $time );
            $use_year = strpos( $query->query_vars[‘s’], $year ) !== false ? true : false;
            $month = date( ‘m’, $time );
            $day = date( ‘d’, $time );
            $date_query = array(
            ‘month’ => $month,
            ‘day’ => $day,
            );
            if ( $use_year ) {
            $date_query[‘year’] = $year;
            }
            $query->set( ‘date_query’, $date_query );
            $query->query_vars[‘s’] = ”;
            }
            return $query;
            }

            /* Search / Relevanssi / Year only filter
            ————————————————————————————— */
            add_filter( ‘relevanssi_modify_wp_query’, function( $query ) {
            $m = preg_match( ‘/\b\d{4}\b/’, $query->query_vars[‘s’], $matches );
            if ( 1 === $m ) {
            $year = $matches[0];
            if ( $year > 1900 && $year query_vars[‘s’];

            $query->query_vars[‘year’] = $year;
            $query->query_vars[‘s’] = str_replace( $year, ”, $query->query_vars[‘s’] );
            }
            }
            return $query;
            } );

          3. Gordon, ah, I see the comments here break the code. If everything’s broken as in fatal error, make sure the code looks like what’s here.

            If there’s no fatal error but things don’t work, please explain how it’s not working. I tested this on my test site and it’s working for me: a year search works, and if it’s a full date, that too works.

          4. Ok so… here’s an image file of the two functions…

            https://www.lonelyshoes.co.uk/screengrabs/functions-v001.png

            please try the search on here https://www.lonelyshoes.co.uk

            If I search ‘blue’ for example I get results

            https://www.lonelyshoes.co.uk/?s=blue

            (Normally at the top of the page I would see “Found:” – followed by the search term which is now blank)

            If I search ‘2024’ – I get no results at all

            If I search ‘January+2024’ – I get no results

            https://www.lonelyshoes.co.uk/?s=January+2024

          5. Gordon, ok, I see. First of all, the missing search term is a problem in my code. I updated the second function here so that it doesn’t remove the search terms accidentally.

            “January 2024” finds no results, because you don’t have a post on January 1st. The natural language date query function doesn’t understand using only months, it’s either day, month, year or day, month (in the current year). So “november 17th 2019” works, because you have a post on that date. It’s possible to modify the function so that it ignores days.

            The I built the year filter is built under the assumption there’s also a search term. If you search for “shoe 2024”, you get results. It should also work with just a year number; not sure why it’s not working on your site, and I can’t tell just from the front end.

          6. Thanks again for helping with this and taking the time to explain. I understand better now what kind of search terms I can use with the natural ‘Natural-language date query’ . It’s not exactly what I was looking for, as I thought it might be interesting to see all the shoes that were found in April across all the years, or July the 4th across the years. Rather than to search a specific day, month and year. However by using your second function on it’s own I it appears I can now search for shoes via a single year search, which is quite good fun. So if I search for shoes using the term ‘2024’ I only get results for posts from ‘2024’. Thanks again and keep up the good work. It’s a great search system – I’m revisiting it after not using it for a few years. Best Gordon

Leave a Reply

Are you a Relevanssi Premium customer looking for support? Please use the Premium support form.

Your email address will not be published. Required fields are marked *