R/api-search.R

Defines functions mobdb_search

Documented in mobdb_search

#' Search for feeds across the Mobility Database
#'
#' @description
#' `r lifecycle::badge("experimental")`
#'
#' Perform a text search across feed names, providers, and locations.
#'
#' **Note:** Search is performed on English words and is case insensitive.
#' Word order is not relevant for matching. For example `New York City Transit` will
#' be parsed as `new & york & city & transit`
#'
#' The endpoint used has known issues with relevance ranking.
#' For better results when searching by provider name,
#' consider using [feeds()] with the `provider` parameter.
#'
#' @param query A string. Search query string. Searches across provider names,
#'   feed names, and locations.
#' @param feed_id A string. The unique identifier for the feed (e.g. "mdb-696",
#'   "mdb-1707", "gbfs-lime_vancouver_bc"). When provided, searches only for
#'   this specific feed and all other filter parameters must be omitted.
#' @param data_type A string. Optional filter by data type: "gtfs", "gtfs_rt",
#'   or "gbfs".
#' @param official A logical. If `TRUE`, only return official feeds when
#'   searching by provider/location. If `FALSE`, only return unofficial feeds.
#'   If `NULL` (default), return all feeds regardless of official status.
#' @param status A string. Feed status filter: "active", "deprecated",
#'   "inactive", "development", or "future".
#' @param gtfs_feature A character vector. Filter feeds by their GTFS features. Only valid
#'   for GTFS Schedule feeds.
#'   [GTFS features definitions are defined here.](https://gtfs.org/getting-started/features/overview/)
#' @param gbfs_version A character vector. Comma-separated list of GBFS versions to filter by.
#'   Only valid for GBFS feeds. [GBFS version notes are defined here](https://github.com/MobilityData/gbfs/blob/master/README.md)
#' @param limit An integer. Maximum number of results (default: 50).
#' @param offset An integer. Number of results to skip for pagination (default: 0).
#' @param use_cache A logical. If `TRUE` (default), use cached results if available.
#'   If `FALSE`, always fetch fresh data from the API. Cached data expires after 1 hour.
#'
#' @return A tibble of matching feeds. Note that search results include additional
#'   fields compared to [feeds()]:
#'   * `locations` - List of data frames with geographical information
#'   * `latest_dataset` - Data frame with most recent dataset details and validation
#'   * Core fields (`id`, `provider`, `data_type`, `status`, `source_info`) are the same
#'
#' @examplesIf mobdb_can_run_examples()
#' # Search for transit agencies (Note: results may not be well-ranked)
#' results <- mobdb_search("transit")
#'
#' # Better approach: use feeds() with provider filter
#' bart <- feeds(provider = "BART")
#' mta <- feeds(provider = "MTA New York")
#'
#' # Search with filters
#' gtfs_feeds <- mobdb_search(
#'   "transit",
#'   data_type = "gtfs",
#'   official = TRUE
#' )
#'
#' # Search with pagination
#' first_50 <- mobdb_search("train", limit = 50, offset = 0)
#' next_50 <- mobdb_search("train", limit = 50, offset = 50)
#'
#' # Search for official GTFS feeds only
#' official_feeds <- mobdb_search("metro", official = TRUE, data_type = "gtfs")
#'
#' # Note: For location-specific searches (state/province/city), use feeds() instead:
#' ontario_transit <- feeds(
#'   provider = "transit",
#'   country_code = "CA",
#'   subdivision_name = "Ontario",
#'   data_type = "gtfs"
#' )
#'
#' @export
mobdb_search <- function(query,
                         feed_id = NULL,
                         data_type = NULL,
                         official = NULL,
                         status = NULL,
                         gtfs_feature = NULL,
                         gbfs_version = NULL,
                         limit = 50,
                         offset = 0,
                         use_cache = TRUE) {

  # Check cache first
  if (use_cache) {
    cache_key <- generate_cache_key(
      query = query,
      feed_id = feed_id,
      data_type = data_type,
      official = official,
      status = status,
      gtfs_feature = gtfs_feature,
      gbfs_version = gbfs_version,
      limit = limit,
      offset = offset,
      prefix = "search"
    )
    cached <- read_from_cache(cache_key, max_age = get_cache_ttl("search"))
    if (!is.null(cached)) return(cached)
  }

  if (!is.character(query) || length(query) != 1 || nchar(query) == 0) {
    cli::cli_abort("{.arg query} must be a non-empty character string.")
  }

  # Validate feed_id exclusivity
  if (!is.null(feed_id)) {
    other_params <- c(data_type, official, status, gtfs_feature, gbfs_version)
    if (any(!vapply(other_params, is.null, logical(1)))) {
      cli::cli_abort(c(
        "When {.arg feed_id} is provided, all other filter parameters are ignored.",
        "i" = "Remove other parameters or omit {.arg feed_id} to use filters."
      ))
    }
  }

  # Validate data_type if provided
  if (!is.null(data_type)) {
    data_type <- match.arg(data_type, c("gtfs", "gtfs_rt", "gbfs"))
  }

  # Validate status if provided
  if (!is.null(status)) {
    status <- match.arg(status, c("active", "deprecated", "inactive", "development", "future"))
  }

  # Validate gtfs_feature (GTFS only)
  if (!is.null(gtfs_feature) && !is.null(data_type) && data_type != "gtfs") {
    cli::cli_abort(c(
      "{.arg gtfs_feature} can only be used with GTFS Schedule feeds.",
      "i" = "Set {.code data_type = \"gtfs\"} to use this parameter."
    ))
  }

  # Validate gbfs_version (GBFS only)
  if (!is.null(gbfs_version) && !is.null(data_type) && data_type != "gbfs") {
    cli::cli_abort(c(
      "{.arg gbfs_version} can only be used with GBFS feeds.",
      "i" = "Set {.code data_type = \"gbfs\"} to use this parameter."
    ))
  }

  # Build query parameters
  query_params <- build_query(
    search_query = query,
    feed_id = feed_id,
    data_type = data_type,
    is_official = official,
    status = status,
    feature = gtfs_feature,
    version = gbfs_version,
    limit = limit,
    offset = offset
  )

  # Make request
  req <- mobdb_request("search") |>
    httr2::req_url_query(!!!query_params)

  resp <- httr2::req_perform(req)
  check_rate_limit(resp)

  result <- mobdb_parse_response(resp)

  # Write to cache
  if (use_cache) {
    write_to_cache(result, cache_key)
  }

  result
}

Try the mobdb package in your browser

Any scripts or data that you put into this service are public.

mobdb documentation built on Aug. 29, 2026, 1:07 a.m.