Implementing commute search in your UI

You can integrate commute search into your UI to allow job seekers to search for jobs within a geographic area set by commute time. Commute search estimates commute time based on a user's selected transit mode and the time of day they plan to travel.

  1. Before you can implement commute search, Cloud Talent Solution must be hooked up to your UI. Follow the quickstart guides to set up Cloud Talent Solution.

  2. Commute search uses the address data that you uploaded with your jobs during CTS implementation to calculate commute time. To enable this feature in your existing CTS UI, send a jobs.search request and include a CommuteFilter object in the JobQuery.commuteFilter field. commuteMethod, travelDuration, startCoordinates, and either roadTraffic or departureTime are required fields.

Go

import (
	"context"
	"fmt"
	"io"

	talent "cloud.google.com/go/talent/apiv4beta1"
	"github.com/golang/protobuf/ptypes/duration"
	talentpb "google.golang.org/genproto/googleapis/cloud/talent/v4beta1"
	"google.golang.org/genproto/googleapis/type/latlng"
)

// commuteSearch searches for jobs within commute filter.
func commuteSearch(w io.Writer, projectID, companyID string) error {
	ctx := context.Background()

	// Initialize a jobService client.
	c, err := talent.NewJobClient(ctx)
	if err != nil {
		return fmt.Errorf("talent.NewJobClient: %v", err)
	}

	// Construct a jobQuery request with a commute filter.
	jobQuery := &talentpb.JobQuery{
		CommuteFilter: &talentpb.CommuteFilter{
			CommuteMethod:  talentpb.CommuteMethod_TRANSIT,
			TravelDuration: &duration.Duration{Seconds: 1800},
			StartCoordinates: &latlng.LatLng{
				Latitude:  37.422408,
				Longitude: -122.085609,
			},
		},
	}
	if companyID != "" {
		jobQuery.Companies = []string{fmt.Sprintf("projects/%s/companies/%s", projectID, companyID)}
	}

	// Construct a searchJobs request with a jobQuery.
	req := &talentpb.SearchJobsRequest{
		Parent: fmt.Sprintf("projects/%s", projectID),
		// Make sure to set the RequestMetadata the same as the associated
		// search request.
		RequestMetadata: &talentpb.RequestMetadata{
			// Make sure to hash your userID.
			UserId: "HashedUsrID",
			// Make sure to hash the sessionID.
			SessionId: "HashedSessionID",
			// Domain of the website where the search is conducted.
			Domain: "www.googlesample.com",
		},
		// Set the actual search term as defined in the jobQuery.
		JobQuery: jobQuery,
	}

	resp, err := c.SearchJobs(ctx, req)
	if err != nil {
		return fmt.Errorf("SearchJobs: %v", err)
	}

	for _, job := range resp.GetMatchingJobs() {
		fmt.Fprintf(w, "Matcing Job: %q\n", job.GetJob().GetName())
		fmt.Fprintf(w, "Job address: %v\n", job.GetCommuteInfo().GetJobLocation().GetPostalAddress().GetAddressLines())
	}

	return nil
}

Java

For more on installing and creating a Cloud Talent Solution client, see Cloud Talent Solution Client Libraries.


import com.google.cloud.talent.v4beta1.CommuteFilter;
import com.google.cloud.talent.v4beta1.CommuteMethod;
import com.google.cloud.talent.v4beta1.Job;
import com.google.cloud.talent.v4beta1.JobQuery;
import com.google.cloud.talent.v4beta1.JobServiceClient;
import com.google.cloud.talent.v4beta1.RequestMetadata;
import com.google.cloud.talent.v4beta1.SearchJobsRequest;
import com.google.cloud.talent.v4beta1.SearchJobsResponse;
import com.google.cloud.talent.v4beta1.TenantName;
import com.google.cloud.talent.v4beta1.TenantOrProjectName;
import com.google.protobuf.Duration;
import com.google.type.LatLng;
import java.io.IOException;

public class CommuteSearchJobs {

  public static void searchJobs() throws IOException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "your-project-id";
    String tenantId = "your-tenant-id";
    searchJobs(projectId, tenantId);
  }

  // Search Jobs with histogram queries.
  public static void searchJobs(String projectId, String tenantId) throws IOException {
    try (JobServiceClient jobServiceClient = JobServiceClient.create()) {
      TenantOrProjectName parent = TenantName.of(projectId, tenantId);
      String domain = "www.example.com";
      String sessionId = "Hashed session identifier";
      String userId = "Hashed user identifier";
      RequestMetadata requestMetadata =
          RequestMetadata.newBuilder()
              .setDomain(domain)
              .setSessionId(sessionId)
              .setUserId(userId)
              .build();

      CommuteMethod commuteMethod = CommuteMethod.DRIVING;
      long seconds = 3600L;
      Duration travelDuration = Duration.newBuilder().setSeconds(seconds).build();

      double latitude = 37.422408;
      double longitude = -122.084068;
      LatLng startCoordinates =
          LatLng.newBuilder().setLatitude(latitude).setLongitude(longitude).build();

      CommuteFilter commuteFilter =
          CommuteFilter.newBuilder()
              .setCommuteMethod(commuteMethod)
              .setTravelDuration(travelDuration)
              .setStartCoordinates(startCoordinates)
              .build();

      JobQuery jobQuery = JobQuery.newBuilder().setCommuteFilter(commuteFilter).build();
      SearchJobsRequest request =
          SearchJobsRequest.newBuilder()
              .setParent(parent.toString())
              .setRequestMetadata(requestMetadata)
              .setJobQuery(jobQuery)
              .build();

      for (SearchJobsResponse.MatchingJob responseItem :
          jobServiceClient.searchJobs(request).iterateAll()) {
        System.out.printf("Job summary: %s\n", responseItem.getJobSummary());
        System.out.printf("Job title snippet: %s\n", responseItem.getJobTitleSnippet());
        Job job = responseItem.getJob();
        System.out.printf("Job name: %s\n", job.getName());
        System.out.printf("Job title: %s\n", job.getTitle());
      }
    }
  }
}

PHP

For more on installing and creating a Cloud Talent Solution client, see Cloud Talent Solution Client Libraries.

require __DIR__ . '/vendor/autoload.php';

use Google\Cloud\Talent\V4beta1\JobServiceClient;
use Google\Cloud\Talent\V4beta1\CommuteFilter;
use Google\Cloud\Talent\V4beta1\CommuteMethod;
use Google\Cloud\Talent\V4beta1\JobQuery;
use Google\Cloud\Talent\V4beta1\RequestMetadata;
use Google\Protobuf\Duration;
use Google\Type\LatLng;

/** Search Jobs using commute distance */
function sampleSearchJobs($projectId, $tenantId)
{

    $jobServiceClient = new JobServiceClient();

    // $projectId = 'Your Google Cloud Project ID';
    // $tenantId = 'Your Tenant ID (using tenancy is optional)';
    $formattedParent = $jobServiceClient->tenantName($projectId, $tenantId);
    $domain = 'www.example.com';
    $sessionId = 'Hashed session identifier';
    $userId = 'Hashed user identifier';
    $requestMetadata = new RequestMetadata();
    $requestMetadata->setDomain($domain);
    $requestMetadata->setSessionId($sessionId);
    $requestMetadata->setUserId($userId);
    $commuteMethod = CommuteMethod::TRANSIT;
    $seconds = 1800;
    $travelDuration = new Duration();
    $travelDuration->setSeconds($seconds);
    $latitude = 37.422408;
    $longitude = 122.084068;
    $startCoordinates = new LatLng();
    $startCoordinates->setLatitude($latitude);
    $startCoordinates->setLongitude($longitude);
    $commuteFilter = new CommuteFilter();
    $commuteFilter->setCommuteMethod($commuteMethod);
    $commuteFilter->setTravelDuration($travelDuration);
    $commuteFilter->setStartCoordinates($startCoordinates);
    $jobQuery = new JobQuery();
    $jobQuery->setCommuteFilter($commuteFilter);

    try {
        // Iterate through all elements
        $pagedResponse = $jobServiceClient->searchJobs($formattedParent, $requestMetadata, ['customRankingInfo' => $customRankingInfo, 'orderBy' => $orderBy]);
        foreach ($pagedResponse->iterateAllElements() as $responseItem) {
            printf('Job summary: %s'.PHP_EOL, $responseItem->getJobSummary());
            printf('Job title snippet: %s'.PHP_EOL, $responseItem->getJobTitleSnippet());
            $job = $responseItem->getJob();
            printf('Job name: %s'.PHP_EOL, $job->getName());
            printf('Job title: %s'.PHP_EOL, $job->getTitle());
        }
    } finally {
        $jobServiceClient->close();
    }

}

Python

For more on installing and creating a Cloud Talent Solution client, see Cloud Talent Solution Client Libraries.


from google.cloud import talent_v4beta1
from google.cloud.talent_v4beta1 import enums
import six


def sample_search_jobs(project_id, tenant_id):
    """Search Jobs using commute distance"""

    client = talent_v4beta1.JobServiceClient()

    # project_id = 'Your Google Cloud Project ID'
    # tenant_id = 'Your Tenant ID (using tenancy is optional)'

    if isinstance(project_id, six.binary_type):
        project_id = project_id.decode('utf-8')
    if isinstance(tenant_id, six.binary_type):
        tenant_id = tenant_id.decode('utf-8')
    parent = client.tenant_path(project_id, tenant_id)
    domain = 'www.example.com'
    session_id = 'Hashed session identifier'
    user_id = 'Hashed user identifier'
    request_metadata = {
        'domain': domain,
        'session_id': session_id,
        'user_id': user_id
    }
    commute_method = enums.CommuteMethod.TRANSIT
    seconds = 1800
    travel_duration = {'seconds': seconds}
    latitude = 37.422408
    longitude = 122.084068
    start_coordinates = {'latitude': latitude, 'longitude': longitude}
    commute_filter = {
        'commute_method': commute_method,
        'travel_duration': travel_duration,
        'start_coordinates': start_coordinates
    }
    job_query = {'commute_filter': commute_filter}

    # Iterate over all results
    for response_item in client.search_jobs(parent,
                                            request_metadata,
                                            job_query=job_query):
        print('Job summary: {}'.format(response_item.job_summary))
        print('Job title snippet: {}'.format(response_item.job_title_snippet))
        job = response_item.job
        print('Job name: {}'.format(job.name))
        print('Job title: {}'.format(job.title))


Ruby

For more on installing and creating a Cloud Talent Solution client, see Cloud Talent Solution Client Libraries.


# Search Jobs using commute distance
def sample_search_jobs project_id, tenant_id
  # Instantiate a client
  job_client = Google::Cloud::Talent::JobService.new version: :v4beta1

  # project_id = "Your Google Cloud Project ID"
  # tenant_id = "Your Tenant ID (using tenancy is optional)"
  formatted_parent = job_client.class.tenant_path(project_id, tenant_id)
  domain = "www.example.com"
  session_id = "Hashed session identifier"
  user_id = "Hashed user identifier"
  request_metadata = {
    domain: domain,
    session_id: session_id,
    user_id: user_id
  }
  commute_method = :TRANSIT
  seconds = 1800
  travel_duration = { seconds: seconds }
  latitude = 37.422408
  longitude = -122.084068
  start_coordinates = { latitude: latitude, longitude: longitude }
  commute_filter = {
    commute_method: commute_method,
    travel_duration: travel_duration,
    start_coordinates: start_coordinates
  }
  job_query = { commute_filter: commute_filter }

  # Iterate over all results.
  job_client.search_jobs(formatted_parent, request_metadata, job_query: job_query).each do |element|
    puts "Job summary: #{element.job_summary}"
    puts "Job title snippet: #{element.job_title_snippet}"
    job = element.job
    puts "Job name: #{job.name}"
    puts "Job title: #{job.title}"
  end
end

UI recommendations

  1. Cloud Talent Solution doesn't allow searching by both distance (using the CTS location filter) commute time in the same API call. To allow job seekers to access both options, use a 2-tab approach or similar.

  2. Modify the frontend of your application to ensure that the backend automatically populates a job seeker's relevant information into the commute filter. The backend should call the API as it would in a regular search request.

  3. Include items in your UI:

    • An option to select either a distance search or commute search. For example, your Search UI could look like the sample below:

    • A drop-down menu of commute method options.

    • An option to adjust traffic conditions.

    • The total travel time (the maximum supported travel time is 60 minutes).

    • Commute start time.

  4. The commute time information returned from the API is used to display information to the job seeker. Only relevant jobs located within the designated commute time area are returned in the results list. See the Job Search Best Practices documentation for a discussion of ways to adjust the order and number of jobs returned within this area.

  5. Commute search results are based on historical and aggregated data rather than live traffic conditions. The departureTime traffic conditions are calculated from average traffic conditions at the specified time of day. The BUSY_HOUR/TRAFFIC_FREE options under roadTraffic are average traffic conditions at morning rush hour and midnight, respectively. Users receive the same commute search results no matter what time of day they send a query.

You can leverage Google Maps to generate a map based on the commute time information returned from CTS and embed it into the results returned to a job seeker. The Maps API suite has several options for displaying a map. Some Maps API options are more effective than others. For example, the Google Maps JavaScript Heatmap visualization paired with marker clustering is an effective way to visualize the relevant jobs returned to a job seeker inside the area determined by their set commute preferences. Conversely, Directions Mode does not show all jobs returned in a search request and is not a recommended option.

For more information on implementing a commute-based search, see the Commute Search how-to guide.