// Copyright 2024 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.firestore.v1beta1;

import "google/firestore/v1beta1/document.proto";
import "google/protobuf/wrappers.proto";

option csharp_namespace = "Google.Cloud.Firestore.V1Beta1";
option go_package = "cloud.google.com/go/firestore/apiv1beta1/firestorepb;firestorepb";
option java_multiple_files = true;
option java_outer_classname = "QueryProto";
option java_package = "com.google.firestore.v1beta1";
option objc_class_prefix = "GCFS";
option php_namespace = "Google\\Cloud\\Firestore\\V1beta1";
option ruby_package = "Google::Cloud::Firestore::V1beta1";

// A Firestore query.
message StructuredQuery {
  // A selection of a collection, such as `messages as m1`.
  message CollectionSelector {
    // The collection ID.
    // When set, selects only collections with this ID.
    string collection_id = 2;

    // When false, selects only collections that are immediate children of
    // the `parent` specified in the containing `RunQueryRequest`.
    // When true, selects all descendant collections.
    bool all_descendants = 3;
  }

  // A filter.
  message Filter {
    // The type of filter.
    oneof filter_type {
      // A composite filter.
      CompositeFilter composite_filter = 1;

      // A filter on a document field.
      FieldFilter field_filter = 2;

      // A filter that takes exactly one argument.
      UnaryFilter unary_filter = 3;
    }
  }

  // A filter that merges multiple other filters using the given operator.
  message CompositeFilter {
    // A composite filter operator.
    enum Operator {
      // Unspecified. This value must not be used.
      OPERATOR_UNSPECIFIED = 0;

      // The results are required to satisfy each of the combined filters.
      AND = 1;
    }

    // The operator for combining multiple filters.
    Operator op = 1;

    // The list of filters to combine.
    // Must contain at least one filter.
    repeated Filter filters = 2;
  }

  // A filter on a specific field.
  message FieldFilter {
    // A field filter operator.
    enum Operator {
      // Unspecified. This value must not be used.
      OPERATOR_UNSPECIFIED = 0;

      // The given `field` is less than the given `value`.
      //
      // Requires:
      //
      // * That `field` come first in `order_by`.
      LESS_THAN = 1;

      // The given `field` is less than or equal to the given `value`.
      //
      // Requires:
      //
      // * That `field` come first in `order_by`.
      LESS_THAN_OR_EQUAL = 2;

      // The given `field` is greater than the given `value`.
      //
      // Requires:
      //
      // * That `field` come first in `order_by`.
      GREATER_THAN = 3;

      // The given `field` is greater than or equal to the given `value`.
      //
      // Requires:
      //
      // * That `field` come first in `order_by`.
      GREATER_THAN_OR_EQUAL = 4;

      // The given `field` is equal to the given `value`.
      EQUAL = 5;

      // The given `field` is not equal to the given `value`.
      //
      // Requires:
      //
      // * No other `NOT_EQUAL`, `NOT_IN`, `IS_NOT_NULL`, or `IS_NOT_NAN`.
      // * That `field` comes first in the `order_by`.
      NOT_EQUAL = 6;

      // The given `field` is an array that contains the given `value`.
      ARRAY_CONTAINS = 7;

      // The given `field` is equal to at least one value in the given array.
      //
      // Requires:
      //
      // * That `value` is a non-empty `ArrayValue` with at most 10 values.
      // * No other `IN` or `ARRAY_CONTAINS_ANY` or `NOT_IN`.
      IN = 8;

      // The given `field` is an array that contains any of the values in the
      // given array.
      //
      // Requires:
      //
      // * That `value` is a non-empty `ArrayValue` with at most 10 values.
      // * No other `IN` or `ARRAY_CONTAINS_ANY` or `NOT_IN`.
      ARRAY_CONTAINS_ANY = 9;

      // The value of the `field` is not in the given array.
      //
      // Requires:
      //
      // * That `value` is a non-empty `ArrayValue` with at most 10 values.
      // * No other `IN`, `ARRAY_CONTAINS_ANY`, `NOT_IN`, `NOT_EQUAL`,
      //   `IS_NOT_NULL`, or `IS_NOT_NAN`.
      // * That `field` comes first in the `order_by`.
      NOT_IN = 10;
    }

    // The field to filter by.
    FieldReference field = 1;

    // The operator to filter by.
    Operator op = 2;

    // The value to compare to.
    Value value = 3;
  }

  // A filter with a single operand.
  message UnaryFilter {
    // A unary operator.
    enum Operator {
      // Unspecified. This value must not be used.
      OPERATOR_UNSPECIFIED = 0;

      // The given `field` is equal to `NaN`.
      IS_NAN = 2;

      // The given `field` is equal to `NULL`.
      IS_NULL = 3;

      // The given `field` is not equal to `NaN`.
      //
      // Requires:
      //
      // * No other `NOT_EQUAL`, `NOT_IN`, `IS_NOT_NULL`, or `IS_NOT_NAN`.
      // * That `field` comes first in the `order_by`.
      IS_NOT_NAN = 4;

      // The given `field` is not equal to `NULL`.
      //
      // Requires:
      //
      // * A single `NOT_EQUAL`, `NOT_IN`, `IS_NOT_NULL`, or `IS_NOT_NAN`.
      // * That `field` comes first in the `order_by`.
      IS_NOT_NULL = 5;
    }

    // The unary operator to apply.
    Operator op = 1;

    // The argument to the filter.
    oneof operand_type {
      // The field to which to apply the operator.
      FieldReference field = 2;
    }
  }

  // A reference to a field, such as `max(messages.time) as max_time`.
  message FieldReference {
    string field_path = 2;
  }

  // An order on a field.
  message Order {
    // The field to order by.
    FieldReference field = 1;

    // The direction to order by. Defaults to `ASCENDING`.
    Direction direction = 2;
  }

  // The projection of document's fields to return.
  message Projection {
    // The fields to return.
    //
    // If empty, all fields are returned. To only return the name
    // of the document, use `['__name__']`.
    repeated FieldReference fields = 2;
  }

  // A sort direction.
  enum Direction {
    // Unspecified.
    DIRECTION_UNSPECIFIED = 0;

    // Ascending.
    ASCENDING = 1;

    // Descending.
    DESCENDING = 2;
  }

  // The projection to return.
  Projection select = 1;

  // The collections to query.
  repeated CollectionSelector from = 2;

  // The filter to apply.
  Filter where = 3;

  // The order to apply to the query results.
  //
  // Firestore guarantees a stable ordering through the following rules:
  //
  //  * Any field required to appear in `order_by`, that is not already
  //    specified in `order_by`, is appended to the order in field name order
  //    by default.
  //  * If an order on `__name__` is not specified, it is appended by default.
  //
  // Fields are appended with the same sort direction as the last order
  // specified, or 'ASCENDING' if no order was specified. For example:
  //
  //  * `SELECT * FROM Foo ORDER BY A` becomes
  //    `SELECT * FROM Foo ORDER BY A, __name__`
  //  * `SELECT * FROM Foo ORDER BY A DESC` becomes
  //    `SELECT * FROM Foo ORDER BY A DESC, __name__ DESC`
  //  * `SELECT * FROM Foo WHERE A > 1` becomes
  //    `SELECT * FROM Foo WHERE A > 1 ORDER BY A, __name__`
  repeated Order order_by = 4;

  // A starting point for the query results.
  Cursor start_at = 7;

  // A end point for the query results.
  Cursor end_at = 8;

  // The number of results to skip.
  //
  // Applies before limit, but after all other constraints. Must be >= 0 if
  // specified.
  int32 offset = 6;

  // The maximum number of results to return.
  //
  // Applies after all other constraints.
  // Must be >= 0 if specified.
  google.protobuf.Int32Value limit = 5;
}

// A position in a query result set.
message Cursor {
  // The values that represent a position, in the order they appear in
  // the order by clause of a query.
  //
  // Can contain fewer values than specified in the order by clause.
  repeated Value values = 1;

  // If the position is just before or just after the given values, relative
  // to the sort order defined by the query.
  bool before = 2;
}
