// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package google.cloud.visionai.v1alpha1;

import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/longrunning/operations.proto";
import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
import "google/rpc/status.proto";
import "google/type/datetime.proto";

option csharp_namespace = "Google.Cloud.VisionAI.V1Alpha1";
option go_package = "cloud.google.com/go/visionai/apiv1alpha1/visionaipb;visionaipb";
option java_multiple_files = true;
option java_outer_classname = "WarehouseProto";
option java_package = "com.google.cloud.visionai.v1alpha1";
option php_namespace = "Google\\Cloud\\VisionAI\\V1alpha1";
option ruby_package = "Google::Cloud::VisionAI::V1alpha1";

// Service that manages media content + metadata for streaming.
service Warehouse {
  option (google.api.default_host) = "visionai.googleapis.com";
  option (google.api.oauth_scopes) = "https://www.googleapis.com/auth/cloud-platform";

  // Creates an asset inside corpus.
  rpc CreateAsset(CreateAssetRequest) returns (Asset) {
    option (google.api.http) = {
      post: "/v1alpha1/{parent=projects/*/locations/*/corpora/*}/assets"
      body: "asset"
    };
    option (google.api.method_signature) = "parent,asset,asset_id";
  }

  // Updates an asset inside corpus.
  rpc UpdateAsset(UpdateAssetRequest) returns (Asset) {
    option (google.api.http) = {
      patch: "/v1alpha1/{asset.name=projects/*/locations/*/corpora/*/assets/*}"
      body: "asset"
    };
    option (google.api.method_signature) = "asset,update_mask";
  }

  // Reads an asset inside corpus.
  rpc GetAsset(GetAssetRequest) returns (Asset) {
    option (google.api.http) = {
      get: "/v1alpha1/{name=projects/*/locations/*/corpora/*/assets/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists an list of assets inside corpus.
  rpc ListAssets(ListAssetsRequest) returns (ListAssetsResponse) {
    option (google.api.http) = {
      get: "/v1alpha1/{parent=projects/*/locations/*/corpora/*}/assets"
    };
    option (google.api.method_signature) = "parent";
  }

  // Deletes asset inside corpus.
  rpc DeleteAsset(DeleteAssetRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      delete: "/v1alpha1/{name=projects/*/locations/*/corpora/*/assets/*}"
    };
    option (google.api.method_signature) = "name";
    option (google.longrunning.operation_info) = {
      response_type: "google.protobuf.Empty"
      metadata_type: "DeleteAssetMetadata"
    };
  }

  // Creates a corpus inside a project.
  rpc CreateCorpus(CreateCorpusRequest) returns (google.longrunning.Operation) {
    option (google.api.http) = {
      post: "/v1alpha1/{parent=projects/*/locations/*}/corpora"
      body: "corpus"
    };
    option (google.api.method_signature) = "parent,corpus";
    option (google.longrunning.operation_info) = {
      response_type: "Corpus"
      metadata_type: "CreateCorpusMetadata"
    };
  }

  // Gets corpus details inside a project.
  rpc GetCorpus(GetCorpusRequest) returns (Corpus) {
    option (google.api.http) = {
      get: "/v1alpha1/{name=projects/*/locations/*/corpora/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Updates a corpus in a project.
  rpc UpdateCorpus(UpdateCorpusRequest) returns (Corpus) {
    option (google.api.http) = {
      patch: "/v1alpha1/{corpus.name=projects/*/locations/*/corpora/*}"
      body: "corpus"
    };
    option (google.api.method_signature) = "corpus,update_mask";
  }

  // Lists all corpora in a project.
  rpc ListCorpora(ListCorporaRequest) returns (ListCorporaResponse) {
    option (google.api.http) = {
      get: "/v1alpha1/{parent=projects/*/locations/*}/corpora"
    };
    option (google.api.method_signature) = "parent";
  }

  // Deletes a corpus only if its empty.
  // Returns empty response.
  rpc DeleteCorpus(DeleteCorpusRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1alpha1/{name=projects/*/locations/*/corpora/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Creates data schema inside corpus.
  rpc CreateDataSchema(CreateDataSchemaRequest) returns (DataSchema) {
    option (google.api.http) = {
      post: "/v1alpha1/{parent=projects/*/locations/*/corpora/*}/dataSchemas"
      body: "data_schema"
    };
    option (google.api.method_signature) = "parent,data_schema";
  }

  // Updates data schema inside corpus.
  rpc UpdateDataSchema(UpdateDataSchemaRequest) returns (DataSchema) {
    option (google.api.http) = {
      patch: "/v1alpha1/{data_schema.name=projects/*/locations/*/corpora/*/dataSchemas/*}"
      body: "data_schema"
    };
    option (google.api.method_signature) = "data_schema,update_mask";
  }

  // Gets data schema inside corpus.
  rpc GetDataSchema(GetDataSchemaRequest) returns (DataSchema) {
    option (google.api.http) = {
      get: "/v1alpha1/{name=projects/*/locations/*/corpora/*/dataSchemas/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Deletes data schema inside corpus.
  rpc DeleteDataSchema(DeleteDataSchemaRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1alpha1/{name=projects/*/locations/*/corpora/*/dataSchemas/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists a list of data schemas inside corpus.
  rpc ListDataSchemas(ListDataSchemasRequest) returns (ListDataSchemasResponse) {
    option (google.api.http) = {
      get: "/v1alpha1/{parent=projects/*/locations/*/corpora/*}/dataSchemas"
    };
    option (google.api.method_signature) = "parent";
  }

  // Creates annotation inside asset.
  rpc CreateAnnotation(CreateAnnotationRequest) returns (Annotation) {
    option (google.api.http) = {
      post: "/v1alpha1/{parent=projects/*/locations/*/corpora/*/assets/*}/annotations"
      body: "annotation"
    };
    option (google.api.method_signature) = "parent,annotation,annotation_id";
  }

  // Reads annotation inside asset.
  rpc GetAnnotation(GetAnnotationRequest) returns (Annotation) {
    option (google.api.http) = {
      get: "/v1alpha1/{name=projects/*/locations/*/corpora/*/assets/*/annotations/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists a list of annotations inside asset.
  rpc ListAnnotations(ListAnnotationsRequest) returns (ListAnnotationsResponse) {
    option (google.api.http) = {
      get: "/v1alpha1/{parent=projects/*/locations/*/corpora/*/assets/*}/annotations"
    };
    option (google.api.method_signature) = "parent";
  }

  // Updates annotation inside asset.
  rpc UpdateAnnotation(UpdateAnnotationRequest) returns (Annotation) {
    option (google.api.http) = {
      patch: "/v1alpha1/{annotation.name=projects/*/locations/*/corpora/*/assets/*/annotations/*}"
      body: "annotation"
    };
    option (google.api.method_signature) = "annotation,update_mask";
  }

  // Deletes annotation inside asset.
  rpc DeleteAnnotation(DeleteAnnotationRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1alpha1/{name=projects/*/locations/*/corpora/*/assets/*/annotations/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Ingests data for the asset. It is not allowed to ingest a data chunk which
  // is already expired according to TTL.
  // This method is only available via the gRPC API (not HTTP since
  // bi-directional streaming is not supported via HTTP).
  rpc IngestAsset(stream IngestAssetRequest) returns (stream IngestAssetResponse) {
  }

  // Generates clips for downloading. The api takes in a time range, and
  // generates a clip of the first content available after start_time and
  // before end_time, which may overflow beyond these bounds.
  // Returned clips are truncated if the total size of the clips are larger
  // than 100MB.
  rpc ClipAsset(ClipAssetRequest) returns (ClipAssetResponse) {
    option (google.api.http) = {
      post: "/v1alpha1/{name=projects/*/locations/*/corpora/*/assets/*}:clip"
      body: "*"
    };
  }

  // Generates a uri for an HLS manifest. The api takes in a collection of time
  // ranges, and generates a URI for an HLS manifest that covers all the
  // requested time ranges.
  rpc GenerateHlsUri(GenerateHlsUriRequest) returns (GenerateHlsUriResponse) {
    option (google.api.http) = {
      post: "/v1alpha1/{name=projects/*/locations/*/corpora/*/assets/*}:generateHlsUri"
      body: "*"
    };
  }

  // Creates a search configuration inside a corpus.
  //
  // Please follow the rules below to create a valid CreateSearchConfigRequest.
  // --- General Rules ---
  // 1. Request.search_config_id must not be associated with an existing
  //    SearchConfig.
  // 2. Request must contain at least one non-empty search_criteria_property or
  //    facet_property.
  // 3. mapped_fields must not be empty, and must map to existing UGA keys.
  // 4. All mapped_fields must be of the same type.
  // 5. All mapped_fields must share the same granularity.
  // 6. All mapped_fields must share the same semantic SearchConfig match
  //    options.
  // For property-specific rules, please reference the comments for
  // FacetProperty and SearchCriteriaProperty.
  rpc CreateSearchConfig(CreateSearchConfigRequest) returns (SearchConfig) {
    option (google.api.http) = {
      post: "/v1alpha1/{parent=projects/*/locations/*/corpora/*}/searchConfigs"
      body: "search_config"
    };
    option (google.api.method_signature) = "parent,search_config,search_config_id";
  }

  // Updates a search configuration inside a corpus.
  //
  // Please follow the rules below to create a valid UpdateSearchConfigRequest.
  // --- General Rules ---
  // 1. Request.search_configuration.name must already exist.
  // 2. Request must contain at least one non-empty search_criteria_property or
  // facet_property.
  // 3. mapped_fields must not be empty, and must map to existing UGA keys.
  // 4. All mapped_fields must be of the same type.
  // 5. All mapped_fields must share the same granularity.
  // 6. All mapped_fields must share the same semantic SearchConfig match
  //    options.
  // For property-specific rules, please reference the comments for
  // FacetProperty and SearchCriteriaProperty.
  rpc UpdateSearchConfig(UpdateSearchConfigRequest) returns (SearchConfig) {
    option (google.api.http) = {
      patch: "/v1alpha1/{search_config.name=projects/*/locations/*/corpora/*/searchConfigs/*}"
      body: "search_config"
    };
    option (google.api.method_signature) = "search_config,update_mask";
  }

  // Gets a search configuration inside a corpus.
  rpc GetSearchConfig(GetSearchConfigRequest) returns (SearchConfig) {
    option (google.api.http) = {
      get: "/v1alpha1/{name=projects/*/locations/*/corpora/*/searchConfigs/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Deletes a search configuration inside a corpus.
  //
  // For a DeleteSearchConfigRequest to be valid,
  // Request.search_configuration.name must already exist.
  rpc DeleteSearchConfig(DeleteSearchConfigRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1alpha1/{name=projects/*/locations/*/corpora/*/searchConfigs/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists all search configurations inside a corpus.
  rpc ListSearchConfigs(ListSearchConfigsRequest) returns (ListSearchConfigsResponse) {
    option (google.api.http) = {
      get: "/v1alpha1/{parent=projects/*/locations/*/corpora/*}/searchConfigs"
    };
    option (google.api.method_signature) = "parent";
  }

  // Search media asset.
  rpc SearchAssets(SearchAssetsRequest) returns (SearchAssetsResponse) {
    option (google.api.http) = {
      post: "/v1alpha1/{corpus=projects/*/locations/*/corpora/*}:searchAssets"
      body: "*"
    };
  }
}

// Different types for a facet bucket.
enum FacetBucketType {
  // Unspecified type.
  FACET_BUCKET_TYPE_UNSPECIFIED = 0;

  // Value type.
  FACET_BUCKET_TYPE_VALUE = 1;

  // Datetime type.
  FACET_BUCKET_TYPE_DATETIME = 2;

  // Fixed Range type.
  FACET_BUCKET_TYPE_FIXED_RANGE = 3;

  // Custom Range type.
  FACET_BUCKET_TYPE_CUSTOM_RANGE = 4;
}

// Request message for CreateAssetRequest.
message CreateAssetRequest {
  // Required. The parent resource where this asset will be created.
  // Format: projects/*/locations/*/corpora/*
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Corpus"
    }
  ];

  // Required. The asset to create.
  Asset asset = 2 [(google.api.field_behavior) = REQUIRED];

  // Optional. The ID to use for the asset, which will become the final component of
  // the asset's resource name if user choose to specify. Otherwise, asset id
  // will be generated by system.
  //
  // This value should be up to 63 characters, and valid characters
  // are /[a-z][0-9]-/. The first character must be a letter, the last could be
  // a letter or a number.
  optional string asset_id = 3 [(google.api.field_behavior) = OPTIONAL];
}

// Request message for GetAsset.
message GetAssetRequest {
  // Required. The name of the asset to retrieve.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Asset"
    }
  ];
}

// Request message for ListAssets.
message ListAssetsRequest {
  // Required. The parent, which owns this collection of assets.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/Asset"
    }
  ];

  // The maximum number of assets to return. The service may return fewer than
  // this value.
  // If unspecified, at most 50 assets will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListAssets` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListAssets` must match
  // the call that provided the page token.
  string page_token = 3;
}

// Response message for ListAssets.
message ListAssetsResponse {
  // The assets from the specified corpus.
  repeated Asset assets = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Response message for UpdateAsset.
message UpdateAssetRequest {
  // Required. The asset to update.
  //
  // The asset's `name` field is used to identify the asset to be updated.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}
  Asset asset = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for DeleteAsset.
message DeleteAssetRequest {
  // Required. The name of the asset to delete.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Asset"
    }
  ];
}

// An asset is a resource in corpus. It represents a media object inside corpus,
// contains metadata and another resource annotation. Different feature could be
// applied to the asset to generate annotations. User could specified annotation
// related to the target asset.
message Asset {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/Asset"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}"
  };

  // Resource name of the asset.
  // Form:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}`
  string name = 1;

  // The duration for which all media assets, associated metadata, and search
  // documents can exist. If not set, then it will using the default ttl in the
  // parent corpus resource.
  google.protobuf.Duration ttl = 2;
}

// Request message of CreateCorpus API.
message CreateCorpusRequest {
  // Required. Form: `projects/{project_number}/locations/{location_id}`
  string parent = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. The corpus to be created.
  Corpus corpus = 2 [(google.api.field_behavior) = REQUIRED];
}

// Metadata for CreateCorpus API.
message CreateCorpusMetadata {

}

// Corpus is a set of video contents for management. Within a corpus, videos
// share the same data schema. Search is also restricted within a single corpus.
message Corpus {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/Corpus"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}"
  };

  // Resource name of the corpus.
  // Form:
  // `projects/{project_number}/locations/{location_id}/corpora/{corpus_id}`
  string name = 1;

  // Required. The corpus name to shown in the UI. The name can be up to 32 characters
  // long.
  string display_name = 2 [(google.api.field_behavior) = REQUIRED];

  // Optional. Description of the corpus. Can be up to 25000 characters long.
  string description = 3 [(google.api.field_behavior) = OPTIONAL];

  // Required. The default TTL value for all assets under the corpus without a asset level
  // user-defined TTL with a maximum of 10 years. This is required for all
  // corpora.
  google.protobuf.Duration default_ttl = 5 [(google.api.field_behavior) = REQUIRED];
}

// Request message for GetCorpus.
message GetCorpusRequest {
  // Required. The resource name of the corpus to retrieve.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Corpus"
    }
  ];
}

// Request message for UpdateCorpus.
message UpdateCorpusRequest {
  // Required. The corpus which replaces the resource on the server.
  Corpus corpus = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for ListCorpora.
message ListCorporaRequest {
  // Required. The resource name of the project from which to list corpora.
  string parent = 1 [(google.api.field_behavior) = REQUIRED];

  // Requested page size. API may return fewer results than requested.
  // If negative, INVALID_ARGUMENT error will be returned.
  // If unspecified or 0, API will pick a default size, which is 10.
  // If the requested page size is larger than the maximum size, API will pick
  // use the maximum size, which is 20.
  int32 page_size = 2;

  // A token identifying a page of results for the server to return.
  // Typically obtained via [ListCorpora.next_page_token][] of the previous
  // [Warehouse.ListCorpora][google.cloud.visionai.v1alpha1.Warehouse.ListCorpora] call.
  string page_token = 3;
}

// Response message for ListCorpora.
message ListCorporaResponse {
  // The corpora in the project.
  repeated Corpus corpora = 1;

  // A token to retrieve next page of results.
  // Pass to [ListCorporaRequest.page_token][google.cloud.visionai.v1alpha1.ListCorporaRequest.page_token] to obtain that page.
  string next_page_token = 2;
}

// Request message for DeleteCorpus.
message DeleteCorpusRequest {
  // Required. The resource name of the corpus to delete.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Corpus"
    }
  ];
}

// Request message for CreateDataSchema.
message CreateDataSchemaRequest {
  // Required. The parent resource where this data schema will be created.
  // Format: projects/*/locations/*/corpora/*
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Corpus"
    }
  ];

  // Required. The data schema to create.
  DataSchema data_schema = 2 [(google.api.field_behavior) = REQUIRED];
}

// Data schema indicates how the user specified annotation is interpreted in the
// system.
message DataSchema {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/DataSchema"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/dataSchemas/{data_schema}"
  };

  // Resource name of the data schema in the form of:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/dataSchemas/{data_schema}`
  // where {data_schema} part should be the same as the `key` field below.
  string name = 1;

  // Required. The key of this data schema. This key should be matching the key of user
  // specified annotation and unique inside corpus. This value can be up to
  // 63 characters, and valid characters are /[a-z][0-9]-/. The first character
  // must be a letter, the last could be a letter or a number.
  string key = 2 [(google.api.field_behavior) = REQUIRED];

  // The schema details mapping to the key.
  DataSchemaDetails schema_details = 3;
}

// Data schema details indicates the data type and the data struct corresponding
// to the key of user specified annotation.
message DataSchemaDetails {
  // The configuration for `PROTO_ANY` data type.
  message ProtoAnyConfig {
    // The type URI of the proto message.
    string type_uri = 1;
  }

  // The search strategy for annotations value of the `key`.
  message SearchStrategy {
    // The types of search strategies to be applied on the annotation key.
    enum SearchStrategyType {
      // Annotatation values of the `key` above will not be searchable.
      NO_SEARCH = 0;

      // When searching with `key`, the value must be exactly as the annotation
      // value that has been ingested.
      EXACT_SEARCH = 1;

      // When searching with `key`, Warehouse will perform broad search based on
      // semantic of the annotation value.
      SMART_SEARCH = 2;
    }

    // The type of search strategy to be applied on the `key` above.
    // The allowed `search_strategy_type` is different for different data types,
    // which is documented in the DataSchemaDetails.DataType. Specifying
    // unsupported `search_strategy_type` for data types will result in
    // INVALID_ARGUMENT error.
    SearchStrategyType search_strategy_type = 1;
  }

  // Data type of the annotation.
  enum DataType {
    // Unspecified type.
    DATA_TYPE_UNSPECIFIED = 0;

    // Integer type.
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH.
    //   Supports query by IntRangeArray.
    INTEGER = 1;

    // Float type.
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH.
    //   Supports query by FloatRangeArray.
    FLOAT = 2;

    // String type.
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH,
    // - DataSchema.SearchStrategy.SMART_SEARCH.
    STRING = 3;

    // Supported formats:
    // %Y-%m-%dT%H:%M:%E*S%E*z (absl::RFC3339_full)
    // %Y-%m-%dT%H:%M:%E*S
    // %Y-%m-%dT%H:%M%E*z
    // %Y-%m-%dT%H:%M
    // %Y-%m-%dT%H%E*z
    // %Y-%m-%dT%H
    // %Y-%m-%d%E*z
    // %Y-%m-%d
    // %Y-%m
    // %Y
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH.
    //   Supports query by DateTimeRangeArray.
    DATETIME = 5;

    // Geo coordinate type.
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH.
    //   Supports query by GeoLocationArray.
    GEO_COORDINATE = 7;

    // Type to pass any proto as available in annotations.proto. Only use
    // internally.
    // Available proto types and its corresponding search behavior:
    // - ImageObjectDetectionPredictionResult, allows SMART_SEARCH on
    //   display_names and NO_SEARCH.
    // - ClassificationPredictionResult, allows SMART_SEARCH on display_names
    //   and NO_SEARCH.
    // - ImageSegmentationPredictionResult, allows NO_SEARCH.
    // - VideoActionRecognitionPredictionResult, allows SMART_SEARCH on
    //   display_name and NO_SEARCH.
    // - VideoObjectTrackingPredictionResult, allows SMART_SEARCH on
    //   display_name and NO_SEARCH.
    // - VideoClassificationPredictionResult, allows SMART_SEARCH on
    //   display_name and NO_SEARCH.
    // - OccupancyCountingPredictionResult, allows EXACT_SEARCH on
    //   stats.full_frame_count.count and NO_SEARCH.
    // - ObjectDetectionPredictionResult, allows SMART_SEARCH on
    //   identified_boxes.entity.label_string and NO_SEARCH.
    PROTO_ANY = 8;

    // Boolean type.
    // Allowed search strategies:
    // - DataSchema.SearchStrategy.NO_SEARCH,
    // - DataSchema.SearchStrategy.EXACT_SEARCH.
    BOOLEAN = 9;
  }

  // The granularity of annotations under this DataSchema.
  enum Granularity {
    // Unspecified granularity.
    GRANULARITY_UNSPECIFIED = 0;

    // Asset-level granularity (annotations must not contain partition info).
    GRANULARITY_ASSET_LEVEL = 1;

    // Partition-level granularity (annotations must contain partition info).
    GRANULARITY_PARTITION_LEVEL = 2;
  }

  // Type of the annotation.
  DataType type = 1;

  // Config for protobuf any type.
  ProtoAnyConfig proto_any_config = 6;

  // The granularity associated with this DataSchema.
  Granularity granularity = 5;

  // The search strategy to be applied on the `key` above.
  SearchStrategy search_strategy = 7;
}

// Request message for UpdateDataSchema.
message UpdateDataSchemaRequest {
  // Required. The data schema's `name` field is used to identify the data schema to be
  // updated. Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/dataSchemas/{data_schema}
  DataSchema data_schema = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for GetDataSchema.
message GetDataSchemaRequest {
  // Required. The name of the data schema to retrieve.
  // Format:
  // projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/dataSchemas/{data_schema_id}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/DataSchema"
    }
  ];
}

// Request message for DeleteDataSchema.
message DeleteDataSchemaRequest {
  // Required. The name of the data schema to delete.
  // Format:
  // projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/dataSchemas/{data_schema_id}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/DataSchema"
    }
  ];
}

// Request message for ListDataSchemas.
message ListDataSchemasRequest {
  // Required. The parent, which owns this collection of data schemas.
  // Format:
  // projects/{project_number}/locations/{location_id}/corpora/{corpus_id}
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/DataSchema"
    }
  ];

  // The maximum number of data schemas to return. The service may return fewer
  // than this value. If unspecified, at most 50 data schemas will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListDataSchemas` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListDataSchemas` must
  // match the call that provided the page token.
  string page_token = 3;
}

// Response message for ListDataSchemas.
message ListDataSchemasResponse {
  // The data schemas from the specified corpus.
  repeated DataSchema data_schemas = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Request message for CreateAnnotation.
message CreateAnnotationRequest {
  // Required. The parent resource where this annotation will be created.
  // Format: projects/*/locations/*/corpora/*/assets/*
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Asset"
    }
  ];

  // Required. The annotation to create.
  Annotation annotation = 2 [(google.api.field_behavior) = REQUIRED];

  // Optional. The ID to use for the annotation, which will become the final component of
  // the annotation's resource name if user choose to specify. Otherwise,
  // annotation id will be generated by system.
  //
  // This value should be up to 63 characters, and valid characters
  // are /[a-z][0-9]-/. The first character must be a letter, the last could be
  // a letter or a number.
  optional string annotation_id = 3 [(google.api.field_behavior) = OPTIONAL];
}

// An annotation is a resource in asset. It represents a key-value mapping of
// content in asset.
message Annotation {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/Annotation"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}/annotations/{annotation}"
  };

  // Resource name of the annotation.
  // Form:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}/annotations/{annotation}`
  string name = 1;

  // User provided annotation.
  UserSpecifiedAnnotation user_specified_annotation = 2;
}

// Annotation provided by users.
message UserSpecifiedAnnotation {
  // Required. Key of the annotation. The key must be set with type by CreateDataSchema.
  string key = 1 [(google.api.field_behavior) = REQUIRED];

  // Value of the annotation. The value must be able to convert
  // to the type according to the data schema.
  AnnotationValue value = 2;

  // Partition information in time and space for the sub-asset level annotation.
  Partition partition = 3;
}

// Location Coordinate Representation
message GeoCoordinate {
  // Latitude Coordinate. Degrees [-90 .. 90]
  double latitude = 1;

  // Longitude Coordinate. Degrees [-180 .. 180]
  double longitude = 2;
}

// Value of annotation, including all types available in data schema.
message AnnotationValue {
  oneof value {
    // Value of int type annotation.
    int64 int_value = 1;

    // Value of float type annotation.
    float float_value = 2;

    // Value of string type annotation.
    string str_value = 3;

    // Value of date time type annotation.
    string datetime_value = 5;

    // Value of geo coordinate type annotation.
    GeoCoordinate geo_coordinate = 7;

    // Value of any proto value.
    google.protobuf.Any proto_any_value = 8;

    // Value of boolean type annotation.
    bool bool_value = 9;

    // Value of customized struct annotation.
    google.protobuf.Struct customized_struct_data_value = 10;
  }
}

// Request message for GetAnnotation API.
message ListAnnotationsRequest {
  // The parent, which owns this collection of annotations.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}
  string parent = 1 [(google.api.resource_reference) = {
                       type: "visionai.googleapis.com/Asset"
                     }];

  // The maximum number of annotations to return. The service may return fewer
  // than this value. If unspecified, at most 50 annotations will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListAnnotations` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListAnnotations` must
  // match the call that provided the page token.
  string page_token = 3;

  // The filter applied to the returned list.
  // We only support filtering for the following fields:
  // `partition.temporal_partition.start_time`,
  // `partition.temporal_partition.end_time`, and `key`.
  // Timestamps are specified in the RFC-3339 format, and only one restriction
  // may be applied per field, joined by conjunctions.
  // Format:
  // "partition.temporal_partition.start_time > "2012-04-21T11:30:00-04:00" AND
  // partition.temporal_partition.end_time < "2012-04-22T11:30:00-04:00" AND
  // key = "example_key""
  string filter = 4;
}

// Request message for ListAnnotations API.
message ListAnnotationsResponse {
  // The annotations from the specified asset.
  repeated Annotation annotations = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Request message for GetAnnotation API.
message GetAnnotationRequest {
  // Required. The name of the annotation to retrieve.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}/annotations/{annotation}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Annotation"
    }
  ];
}

// Request message for UpdateAnnotation API.
message UpdateAnnotationRequest {
  // Required. The annotation to update.
  // The annotation's `name` field is used to identify the annotation to be
  // updated. Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}/annotations/{annotation}
  Annotation annotation = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for DeleteAnnotation API.
message DeleteAnnotationRequest {
  // Required. The name of the annotation to delete.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/assets/{asset}/annotations/{annotation}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Annotation"
    }
  ];
}

// Request message for CreateSearchConfig.
message CreateSearchConfigRequest {
  // Required. The parent resource where this search configuration will be created.
  // Format: projects/*/locations/*/corpora/*
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/SearchConfig"
    }
  ];

  // Required. The search config to create.
  SearchConfig search_config = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. ID to use for the new search config. Will become the final component of the
  // SearchConfig's resource name. This value should be up to 63 characters, and
  // valid characters are /[a-z][0-9]-_/. The first character must be a letter,
  // the last could be a letter or a number.
  string search_config_id = 3 [(google.api.field_behavior) = REQUIRED];
}

// Request message for UpdateSearchConfig.
message UpdateSearchConfigRequest {
  // Required. The search configuration to update.
  //
  // The search configuration's `name` field is used to identify the resource to
  // be updated. Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/searchConfigs/{search_config}
  SearchConfig search_config = 1 [(google.api.field_behavior) = REQUIRED];

  // The list of fields to be updated. If left unset, all field paths will be
  // updated/overwritten.
  google.protobuf.FieldMask update_mask = 2;
}

// Request message for GetSearchConfig.
message GetSearchConfigRequest {
  // Required. The name of the search configuration to retrieve.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/searchConfigs/{search_config}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/SearchConfig"
    }
  ];
}

// Request message for DeleteSearchConfig.
message DeleteSearchConfigRequest {
  // Required. The name of the search configuration to delete.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}/searchConfigs/{search_config}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/SearchConfig"
    }
  ];
}

// Request message for ListSearchConfigs.
message ListSearchConfigsRequest {
  // Required. The parent, which owns this collection of search configurations.
  // Format:
  // projects/{project_number}/locations/{location}/corpora/{corpus}
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "visionai.googleapis.com/SearchConfig"
    }
  ];

  // The maximum number of search configurations to return. The service may
  // return fewer than this value. If unspecified, a page size of 50 will be
  // used. The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListSearchConfigs` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to
  // `ListSearchConfigs` must match the call that provided the page
  // token.
  string page_token = 3;
}

// Response message for ListSearchConfigs.
message ListSearchConfigsResponse {
  // The search configurations from the specified corpus.
  repeated SearchConfig search_configs = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// SearchConfig stores different properties that will affect search
// behaviors and search results.
message SearchConfig {
  option (google.api.resource) = {
    type: "visionai.googleapis.com/SearchConfig"
    pattern: "projects/{project_number}/locations/{location}/corpora/{corpus}/searchConfigs/{search_config}"
  };

  // Resource name of the search configuration.
  // For CustomSearchCriteria, search_config would be the search
  // operator name. For Facets, search_config would be the facet
  // dimension name.
  // Form:
  // `projects/{project_number}/locations/{location}/corpora/{corpus}/searchConfigs/{search_config}`
  string name = 1;

  // Establishes a FacetDimension and associated specifications.
  FacetProperty facet_property = 2;

  // Creates a mapping between a custom SearchCriteria and one or more UGA keys.
  SearchCriteriaProperty search_criteria_property = 3;
}

// Central configuration for a facet.
message FacetProperty {
  // If bucket type is FIXED_RANGE, specify how values are bucketized. Use
  // FixedRangeBucketSpec when you want to create multiple buckets with equal
  // granularities. Using integer bucket value as an example, when
  // bucket_start = 0, bucket_granularity = 10, bucket_count = 5, this facet
  // will be aggregated via the following buckets:
  // [-inf, 0), [0, 10), [10, 20), [20, 30), [30, inf).
  // Notably, bucket_count <= 1 is an invalid spec.
  message FixedRangeBucketSpec {
    // Lower bound of the bucket. NOTE: Only integer type is currently supported
    // for this field.
    FacetValue bucket_start = 1;

    // Bucket granularity. NOTE: Only integer type is currently supported for
    // this field.
    FacetValue bucket_granularity = 2;

    // Total number of buckets.
    int32 bucket_count = 3;
  }

  // If bucket type is CUSTOM_RANGE, specify how values are bucketized. Use
  // integer bucket value as an example, when the endpoints are 0, 10, 100, and
  // 1000, we will generate the following facets:
  // [-inf, 0), [0, 10), [10, 100), [100, 1000), [1000, inf).
  // Notably:
  // - endpoints must be listed in ascending order. Otherwise, the SearchConfig
  //   API will reject the facet config.
  // - < 1 endpoints is an invalid spec.
  message CustomRangeBucketSpec {
    // Currently, only integer type is supported for this field.
    repeated FacetValue endpoints = 1;
  }

  // If bucket type is DATE, specify how date values are bucketized.
  message DateTimeBucketSpec {
    // Granularity enum for the datetime bucket.
    enum Granularity {
      // Unspecified granularity.
      GRANULARITY_UNSPECIFIED = 0;

      // Granularity is year.
      YEAR = 1;

      // Granularity is month.
      MONTH = 2;

      // Granularity is day.
      DAY = 3;
    }

    // Granularity of date type facet.
    Granularity granularity = 1;
  }

  oneof range_facet_config {
    // Fixed range facet bucket config.
    FixedRangeBucketSpec fixed_range_bucket_spec = 5;

    // Custom range facet bucket config.
    CustomRangeBucketSpec custom_range_bucket_spec = 6;

    // Datetime range facet bucket config.
    DateTimeBucketSpec datetime_bucket_spec = 7;
  }

  // Name of the facets, which are the dimensions users want to use to refine
  // search results. `mapped_fields` will match UserSpecifiedDataSchema keys.
  //
  // For example, user can add a bunch of UGAs with the same key, such as
  // player:adam, player:bob, player:charles. When multiple mapped_fields are
  // specified, will merge their value together as final facet value. E.g.
  // home_team: a, home_team:b, away_team:a, away_team:c, when facet_field =
  // [home_team, away_team], facet_value will be [a, b, c].
  //
  // UNLESS this is a 1:1 facet dimension (mapped_fields.size() == 1) AND the
  // mapped_field equals the parent SearchConfig.name, the parent must
  // also contain a SearchCriteriaProperty that maps to the same fields.
  // mapped_fields must not be empty.
  repeated string mapped_fields = 1;

  // Display name of the facet. To be used by UI for facet rendering.
  string display_name = 2;

  // Maximum number of unique bucket to return for one facet. Bucket number can
  // be large for high-cardinality facet such as "player". We only return top-n
  // most related ones to user. If it's <= 0, the server will decide the
  // appropriate result_size.
  int64 result_size = 3;

  // Facet bucket type e.g. value, range.
  FacetBucketType bucket_type = 4;
}

// Central configuration for custom search criteria.
message SearchCriteriaProperty {
  // Each mapped_field corresponds to a UGA key. To understand how this property
  // works, take the following example. In the SearchConfig table, the
  // user adds this entry:
  //   search_config {
  //     name: "person"
  //     search_criteria_property {
  //       mapped_fields: "player"
  //       mapped_fields: "coach"
  //     }
  //   }
  //
  // Now, when a user issues a query like:
  //   criteria {
  //     field: "person"
  //     text_array {
  //       txt_values: "Tom Brady"
  //       txt_values: "Bill Belichick"
  //     }
  //   }
  //
  // MWH search will return search documents where (player=Tom Brady ||
  // coach=Tom Brady || player=Bill Belichick || coach=Bill Belichick).
  repeated string mapped_fields = 1;
}

// Definition of a single value with generic type.
message FacetValue {
  oneof value {
    // String type value.
    string string_value = 1;

    // Integer type value.
    int64 integer_value = 2;

    // Datetime type value.
    google.type.DateTime datetime_value = 3;
  }
}

// Holds the facet value, selections state, and metadata.
message FacetBucket {
  // The range of values [start, end) for which faceting is applied.
  message Range {
    // Start of the range. Non-existence indicates some bound (e.g. -inf).
    FacetValue start = 1;

    // End of the range. Non-existence indicates some bound (e.g. inf).
    FacetValue end = 2;
  }

  // Bucket associated with a facet. For example, bucket of facet “team”
  // can be "49ers", "patriots", etc; bucket of facet "player" can be "tom
  // brady", "drew brees", etc.
  oneof bucket_value {
    // Singular value.
    FacetValue value = 2;

    // Range value.
    Range range = 4;
  }

  // Whether one facet bucket is selected. This field represents user's facet
  // selection. It is set by frontend in SearchVideosRequest.
  bool selected = 3;
}

// A group of facet buckets to be passed back and forth between backend &
// frontend.
message FacetGroup {
  // Unique id of the facet group.
  string facet_id = 1;

  // Display name of the facet. To be used by UI for facet rendering.
  string display_name = 2;

  // Buckets associated with the facet. E.g. for "Team" facet, the bucket
  // can be 49ers, patriots, etc.
  repeated FacetBucket buckets = 3;

  // Facet bucket type.
  FacetBucketType bucket_type = 4;

  // If true, return query matched annotations for this facet group's selection.
  // This option is only applicable for facets based on partition level
  // annotations. It supports the following facet values:
  //  - INTEGER
  //  - STRING (DataSchema.SearchStrategy.EXACT_SEARCH only)
  bool fetch_matched_annotations = 5;
}

// Request message for IngestAsset API.
message IngestAssetRequest {
  // Configuration for the data.
  message Config {
    // Type information for video data.
    message VideoType {
      // Container format of the video.
      enum ContainerFormat {
        // The default type, not supposed to be used.
        CONTAINER_FORMAT_UNSPECIFIED = 0;

        // Mp4 container format.
        CONTAINER_FORMAT_MP4 = 1;
      }

      // Container format of the video data.
      ContainerFormat container_format = 1;
    }

    oneof data_type {
      // Type information for video data.
      VideoType video_type = 2;
    }

    // Required. The resource name of the asset that the ingested data belongs to.
    string asset = 1 [
      (google.api.field_behavior) = REQUIRED,
      (google.api.resource_reference) = {
        type: "visionai.googleapis.com/Asset"
      }
    ];
  }

  // Contains the data and the corresponding time range this data is for.
  message TimeIndexedData {
    // Data to be ingested.
    bytes data = 1;

    // Time range of the data.
    Partition.TemporalPartition temporal_partition = 2;
  }

  oneof streaming_request {
    // Provides information for the data and the asset resource name that the
    // data belongs to. The first `IngestAssetRequest` message must only contain
    // a `Config` message.
    Config config = 1;

    // Data to be ingested.
    TimeIndexedData time_indexed_data = 2;
  }
}

// Response message for IngestAsset API.
message IngestAssetResponse {
  // Time range of the data that has been successfully ingested.
  Partition.TemporalPartition successfully_ingested_partition = 1;
}

// Request message for ClipAsset API.
message ClipAssetRequest {
  // Required. The resource name of the asset to request clips for.
  // Form:
  // 'projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}'
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Asset"
    }
  ];

  // Required. The time range to request clips for.
  Partition.TemporalPartition temporal_partition = 2 [(google.api.field_behavior) = REQUIRED];
}

// Response message for ClipAsset API.
message ClipAssetResponse {
  // Signed uri with corresponding time range.
  message TimeIndexedUri {
    // Time range of the video that the uri is for.
    Partition.TemporalPartition temporal_partition = 1;

    // Signed uri to download the video clip.
    string uri = 2;
  }

  // A list of signed uris to download the video clips that cover the requested
  // time range ordered by time.
  repeated TimeIndexedUri time_indexed_uris = 1;
}

// Request message for GenerateHlsUri API.
message GenerateHlsUriRequest {
  // Required. The resource name of the asset to request clips for.
  // Form:
  // 'projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}'
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Asset"
    }
  ];

  // Required. The time range to request clips for.
  repeated Partition.TemporalPartition temporal_partitions = 2 [(google.api.field_behavior) = REQUIRED];
}

// Response message for GenerateHlsUri API.
message GenerateHlsUriResponse {
  // A signed uri to download the HLS manifest corresponding to the requested
  // times.
  string uri = 1;

  // A list of temporal partitions of the content returned in the order they
  // appear in the stream.
  repeated Partition.TemporalPartition temporal_partitions = 2;
}

// Request message for SearchAssets.
message SearchAssetsRequest {
  // Required. The parent corpus to search.
  // Form: `projects/{project_id}/locations/{location_id}/corpora/{corpus_id}'
  string corpus = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "visionai.googleapis.com/Corpus"
    }
  ];

  // The number of results to be returned in this page. If it's 0, the server
  // will decide the appropriate page_size.
  int32 page_size = 2;

  // The continuation token to fetch the next page. If empty, it means it is
  // fetching the first page.
  string page_token = 3;

  // Time ranges that matching video content must fall within. If no ranges are
  // provided, there will be no time restriction. This field is treated just
  // like the criteria below, but defined separately for convenience as it is
  // used frequently. Note that if the end_time is in the future, it will be
  // clamped to the time the request was received.
  DateTimeRangeArray content_time_ranges = 5;

  // Criteria applied to search results.
  repeated Criteria criteria = 4;

  // Stores most recent facet selection state. Only facet groups with user's
  // selection will be presented here. Selection state is either selected or
  // unselected. Only selected facet buckets will be used as search criteria.
  repeated FacetGroup facet_selections = 6;

  // A list of annotation keys to specify the annotations to be retrieved and
  // returned with each search result.
  // Annotation granularity must be GRANULARITY_ASSET_LEVEL and its search
  // strategy must not be NO_SEARCH.
  repeated string result_annotation_keys = 8;
}

// The metadata for DeleteAsset API that embeds in
// [metadata][google.longrunning.Operation.metadata] field.
message DeleteAssetMetadata {

}

// Stores the criteria-annotation matching results for each search result item.
message AnnotationMatchingResult {
  // The criteria used for matching. It can be an input search criteria or a
  // criteria converted from a facet selection.
  Criteria criteria = 1;

  // Matched annotations for the criteria.
  repeated Annotation matched_annotations = 2;

  // Status of the match result. Possible values:
  // FAILED_PRECONDITION - the criteria is not eligible for match.
  // OK - matching is performed.
  google.rpc.Status status = 3;
}

// Search result contains asset name and corresponding time ranges.
message SearchResultItem {
  // The resource name of the asset.
  // Form:
  // 'projects/{project_number}/locations/{location_id}/corpora/{corpus_id}/assets/{asset_id}'
  string asset = 1;

  // The matched asset segments.
  // Deprecated: please use singular `segment` field.
  repeated Partition.TemporalPartition segments = 2 [deprecated = true];

  // The matched asset segment.
  Partition.TemporalPartition segment = 5;

  // Search result annotations specified by result_annotation_keys in search
  // request.
  repeated Annotation requested_annotations = 3;

  // Criteria or facet-selection based annotation matching results associated to
  // this search result item. Only contains results for criteria or
  // facet_selections with fetch_matched_annotations=true.
  repeated AnnotationMatchingResult annotation_matching_results = 4;
}

// Response message for SearchAssets.
message SearchAssetsResponse {
  // Returned search results.
  repeated SearchResultItem search_result_items = 1;

  // The next-page continuation token.
  string next_page_token = 2;

  // Facet search results of a given query, which contains user's
  // already-selected facet values and updated facet search results.
  repeated FacetGroup facet_results = 3;
}

// Integer range type.
message IntRange {
  // Start of the int range.
  optional int64 start = 1;

  // End of the int range.
  optional int64 end = 2;
}

// Float range type.
message FloatRange {
  // Start of the float range.
  optional float start = 1;

  // End of the float range.
  optional float end = 2;
}

// A list of string-type values.
message StringArray {
  // String type values.
  repeated string txt_values = 1;
}

// A list of integer range values.
message IntRangeArray {
  // Int range values.
  repeated IntRange int_ranges = 1;
}

// A list of float range values.
message FloatRangeArray {
  // Float range values.
  repeated FloatRange float_ranges = 1;
}

// Datetime range type.
message DateTimeRange {
  // Start date time.
  google.type.DateTime start = 1;

  // End data time.
  google.type.DateTime end = 2;
}

// A list of datetime range values.
message DateTimeRangeArray {
  // Date time ranges.
  repeated DateTimeRange date_time_ranges = 1;
}

// Representation of a circle area.
message CircleArea {
  // Latitude of circle area's center. Degrees [-90 .. 90]
  double latitude = 1;

  // Longitude of circle area's center. Degrees [-180 .. 180]
  double longitude = 2;

  // Radius of the circle area in meters.
  double radius_meter = 3;
}

// A list of locations.
message GeoLocationArray {
  // A list of circle areas.
  repeated CircleArea circle_areas = 1;
}

message BoolValue {
  bool value = 1;
}

// Filter criteria applied to current search results.
message Criteria {
  oneof value {
    // The text values associated with the field.
    StringArray text_array = 2;

    // The integer ranges associated with the field.
    IntRangeArray int_range_array = 3;

    // The float ranges associated with the field.
    FloatRangeArray float_range_array = 4;

    // The datetime ranges associated with the field.
    DateTimeRangeArray date_time_range_array = 5;

    // Geo Location array.
    GeoLocationArray geo_location_array = 6;

    // A Boolean value.
    BoolValue bool_value = 7;
  }

  // The UGA field or ML field to apply filtering criteria.
  string field = 1;

  // If true, return query matched annotations for this criteria.
  // This option is only applicable for partition level annotations and supports
  // the following data types:
  //  - INTEGER
  //  - FLOAT
  //  - STRING (DataSchema.SearchStrategy.EXACT_SEARCH only)
  //  - BOOLEAN
  bool fetch_matched_annotations = 8;
}

// Partition to specify the partition in time and space for sub-asset level
// annotation.
message Partition {
  // Partition of asset in UTC Epoch time.
  message TemporalPartition {
    // Start time of the partition.
    google.protobuf.Timestamp start_time = 1;

    // End time of the partition.
    google.protobuf.Timestamp end_time = 2;
  }

  // Partition of asset in space.
  message SpatialPartition {
    // The minimum x coordinate value.
    optional int64 x_min = 1;

    // The minimum y coordinate value.
    optional int64 y_min = 2;

    // The maximum x coordinate value.
    optional int64 x_max = 3;

    // The maximum y coordinate value.
    optional int64 y_max = 4;
  }

  // Partition of asset in time.
  TemporalPartition temporal_partition = 1;

  // Partition of asset in space.
  SpatialPartition spatial_partition = 2;
}
