We came across this issue when we wanted to exclude a number of categories in particular from our WooCommerce shop landing page. You can add as many category exclusions as you want by adding onto the array called aHideThese below. Alternatively you could use this function to include only certain categories by following the second example.

Hide Category Products

Just alter the $aHideThese array you see below to customise which category products are shown.

// Hide categories from /shop
add_action( 'pre_get_posts', 'cf_custom_pre_get_woocommerce_query' );
if(!function_exists('cf_custom_pre_get_woocommerce_query')) {
	function cf_custom_pre_get_woocommerce_query( $oQuery ) {

		if ( !$oQuery->is_main_query() AND !$oQuery->is_post_type_archive() AND !is_admin() ) {
			return;
		}

		if (is_shop()) {
			$aHideThese = array('maintenance','hosting');
			$oQuery->set( 'tax_query', array(array(
				'taxonomy' => 'product_cat',
				'field' => 'slug',
				'terms' => $aHideThese,
				'operator' => 'NOT IN'
			)));
		}
		remove_action( 'pre_get_posts', 'cf_custom_pre_get_woocommerce_query' );
	}
}

Show Category Products

Just alter the $aShowThese array you see below to customise which category products are shown.

// Show categories from /shop
add_action( 'pre_get_posts', 'cf_custom_pre_get_woocommerce_query' );
if(!function_exists('cf_custom_pre_get_woocommerce_query')) {
	function cf_custom_pre_get_woocommerce_query( $oQuery ) {

		if ( !$oQuery->is_main_query() AND !$oQuery->is_post_type_archive() AND !is_admin() ) {
			return;
		}

		if (is_shop()) {
			$aShowThese = array('maintenance');
			$oQuery->set( 'tax_query', array(array(
				'taxonomy' => 'product_cat',
				'field' => 'slug',
				'terms' => $aShowThese,
				'operator' => 'IN'
			)));
		}
		remove_action( 'pre_get_posts', 'cf_custom_pre_get_woocommerce_query' );
	}
}