syntax = "proto3";
package summa.proto;

import "google/protobuf/empty.proto";
import "summa/proto/utils.proto";


// Main entrypoint for `Summa` that accepts queries to execute
service SearchApi {
  rpc search (SearchRequest) returns (SearchResponse) {}
}

// Requests
message SearchRequest {
  // The index name or alias
  string index_alias = 1;
  // Query DSL. Use `MatchQuery` to pass a free-form query
  Query query = 2;
  // Every collector is responsible of processing and storing documents and/or their derivatives (like counters)
  // to return them to the caller
  repeated Collector collectors = 3;
  // Extra fields used only for logging purposes
  map<string, string> tags = 4;
}

message SearchResponse {
  // The real index name got through alias resolution
  string index_name = 1;
  // An array of collector outputs
  repeated CollectorOutput collector_outputs = 2;
  // Time spent inside of `search` handler
  double elapsed_secs = 3;
}

// Recursive query DSL
message Query {
  oneof query {
    BooleanQuery boolean = 1;
    MatchQuery match = 2;
    RegexQuery regex = 3;
    TermQuery term = 4;
    PhraseQuery phrase = 5;
    RangeQuery range = 6;
    AllQuery all = 7;
    MoreLikeThisQuery more_like_this = 8;
    BoostQuery boost = 9;
  }
}

message AllQuery {}

message BoostQuery {
  Query query = 1;
  string score = 2;
}

message MoreLikeThisQuery {
  string document = 1;
  optional uint64 min_doc_frequency = 2;
  optional uint64 max_doc_frequency = 3;
  optional uint64 min_term_frequency = 4;
  optional uint64 max_query_terms = 5;
  optional uint64 min_word_length = 6;
  optional uint64 max_word_length = 7;
  optional string boost = 8;
  repeated string stop_words = 9;
}

message PhraseQuery {
  string field = 1;
  string value = 2;
  uint32 slop = 3;
}

message RangeQuery {
  string field = 1;
  Range value = 2;
}

message MatchQuery {
  string value = 1;
}

message BooleanSubquery {
  Occur occur = 1;
  Query query = 2;
}

message BooleanQuery {
  repeated BooleanSubquery subqueries = 1;
}

message RegexQuery {
  string field = 1;
  string value = 2;
}

message TermQuery {
  string field = 1;
  string value = 2;
}

// Aggregation
message Aggregation {
  oneof aggregation {
    BucketAggregation bucket = 1;
    MetricAggregation metric = 2;
  }
}

message BucketAggregation {
  oneof bucket_agg {
    RangeAggregation range = 1;
    HistogramAggregation histogram = 2;
    TermsAggregation terms = 3;
  };
  map<string, Aggregation> sub_aggregation = 4;
}

message RangeAggregation {
  string field = 1;
  repeated RangeAggregationRange ranges = 2;
}

message RangeAggregationRange {
  optional double from = 1;
  optional double to = 2;
}

message HistogramAggregation {
    string field = 1;
    double interval = 2;
    optional double offset = 3;
    optional uint64 min_doc_count = 4;
    optional HistogramBounds hard_bounds = 5;
    optional HistogramBounds extended_bounds = 6;
}

message HistogramBounds {
  double min = 1;
  double max = 2;
}

message TermsAggregation {
    string field = 1;
    optional uint32 size = 2;
    optional uint32 split_size = 3;
    optional uint32 segment_size = 4;
    optional bool show_term_doc_count_error = 5;
    optional uint64 min_doc_count = 6;
    optional CustomOrder order = 7;
}

message CustomOrder {
    oneof order_target {
      google.protobuf.Empty key = 1;
      google.protobuf.Empty count = 2;
      string sub_aggregation = 3;
    };
    Order order = 4;
}

message MetricAggregation {
  oneof metric_aggregation {
    AverageAggregation average = 1;
    StatsAggregation stats = 2;
  }
}

message AverageAggregation {
  string field = 1;
}

message StatsAggregation {
  string field = 1;
}

// Extra structures

message BucketEntry {
  Key key = 1;
  uint64 doc_count = 2;
  map<string, AggregationResult> sub_aggregation = 3;
}

message Key {
  oneof key {
    string str = 1;
    double f64 = 2;
  }
}

enum Occur {
  should = 0;
  must = 1;
  must_not = 2;
}

message Range {
  string left = 1;
  string right = 2;
  bool including_left = 3;
  bool including_right = 4;
}

message RangeBucketEntry {
  Key key = 1;
  uint64 doc_count = 2;
  map<string, AggregationResult> sub_aggregation = 3;
  optional double from = 4;
  optional double to = 5;
}

message Score {
  oneof score {
    double f64_score = 1;
    uint64 u64_score = 2;
  }
}


message ScoredDocument {
  string document = 1;
  Score score = 2;
  uint32 position = 3;
}

message Scorer {
  oneof scorer {
    string eval_expr = 1;
    string order_by = 2;
  }
}

// Collectors and CollectorOutputs

message Collector {
  oneof collector {
    TopDocsCollector top_docs = 1;
    ReservoirSamplingCollector reservoir_sampling = 2;
    CountCollector count = 3;
    FacetCollector facet = 4;
    AggregationCollector aggregation = 5;
  }
}

message CollectorOutput {
  oneof collector_output {
    TopDocsCollectorOutput top_docs = 1;
    ReservoirSamplingCollectorOutput reservoir_sampling = 2;
    CountCollectorOutput count = 3;
    FacetCollectorOutput facet = 4;
    AggregationCollectorOutput aggregation = 5;
  }
}

message CountCollector {}

message CountCollectorOutput { uint32 count = 1; }

message FacetCollector {
  string field = 1;
  repeated string facets = 2;
}

message FacetCollectorOutput {
  map<string, uint64> facet_counts = 1;
}

message ReservoirSamplingCollector {
  uint32 limit = 1;
}

message ReservoirSamplingCollectorOutput {
  repeated string documents = 1;
}

message TopDocsCollector {
  uint32 limit = 1;
  uint32 offset = 2;
  optional Scorer scorer = 3;
}

message TopDocsCollectorOutput {
  repeated ScoredDocument scored_documents = 1;
  bool has_next = 2;
}

message AggregationCollector {
  map<string, Aggregation> aggregations = 1;
}

message AggregationCollectorOutput {
  map<string, AggregationResult> aggregation_results = 1;
}

message AggregationResult {
  oneof aggregation_result {
    BucketResult bucket = 1;
    MetricResult metric = 2;
  }
}

message BucketResult {
  oneof bucket_result {
    RangeResult range = 1;
    HistogramResult histogram = 2;
    TermsResult terms = 3;
  }
}

message RangeResult {
  repeated RangeBucketEntry buckets = 1;
}

message HistogramResult {
  repeated BucketEntry buckets = 1;
}

message TermsResult {
  repeated BucketEntry buckets = 1;
  uint64 sum_other_doc_count = 2;
  optional uint64 doc_count_error_upper_bound = 3;
}

message MetricResult {
  oneof metric_result {
    SingleMetricResult single_metric = 1;
    StatsResult stats = 2;
  }
}

message SingleMetricResult {
  optional double value = 1;
}

message StatsResult {
    uint64 count = 1;
    double sum = 2;
    optional double standard_deviation = 3;
    optional double min = 4;
    optional double max = 5;
    optional double avg = 6;
}
