enonic / lib-guillotine

Apache License 2.0
3 stars 0 forks source link

Support aggregations #77

Closed sigdestad closed 3 years ago

sigdestad commented 3 years ago

Propose schema for aggregations and how to implement this before we start implementing. We should support all aggs defined by standard API: https://developer.enonic.com/docs/xp/stable/storage/aggregations

anatol-sialitski commented 3 years ago

@sigdestad and @alansemenov please have a look at the schema for aggregations supports. Also you can open it using the Playgound. Please use code from https://github.com/enonic/lib-guillotine/blob/issue-77/src/main/scripts/package.json

npm install
npm start
input Aggregation {
  terms: TermsAggregation
  stats: StatsAggregation
  range: RangeAggregation
  geoDistance: GeoDistanceAggregation
  dateRange: DateRangeAggregation
  dateHistogram: DateHistogramAggregation
  subAggregations: AggregationInput
}

input AggregationInput {
  name: String!
  aggregation: Aggregation!
}

type AggregationResult {
  name: String!
  buckets: [AggregationResultInterface]!
}

interface AggregationResultInterface {
  docCount: Int!
  key: String!
}

interface Content {
  _id: ID!
  _name: String!
  _path: String!
  _references: [Content]
  displayName: String
}

input DateHistogramAggregation {
  field: String!
  interval: String
  format: String
  minDocCount: Int
}

type DateHistogramAggregationResult implements AggregationResultInterface {
  docCount: Int!
  key: String!
}

input DateRange {
  from: String
  to: String
}

input DateRangeAggregation {
  field: String!
  format: String
  ranges: [DateRange]
  range: DateRange
}

type DateRangeAggregationResult implements AggregationResultInterface {
  docCount: Int!
  key: String!
  from: String
  to: String
}

input GeoDistanceAggregation {
  field: String!
  ranges: [Range]
  range: Range
  unit: String
  origin: GeoPoint
}

type GeoDistanceAggregationResult implements AggregationResultInterface {
  docCount: Int!
  key: String!
}

input GeoPoint {
  lat: Float!
  lon: Float!
}

type Query {
  query(aggregations: AggregationInput): QueryResult
}

type QueryResult {
  contents: [Content]
  aggregations: AggregationResult
}

input Range {
  from: Float
  to: Float
}

input RangeAggregation {
  field: String!
  ranges: [Range]
  range: Range
}

type RangeAggregationResult implements AggregationResultInterface {
  docCount: Int!
  key: String!
  from: Float
  to: Float
}

type Stats {
  count: Int!
  min: Int!
  max: Int!
  avg: Int!
  sum: Int!
}

input StatsAggregation {
  field: String!
}

type StatsAggregationResult implements AggregationResultInterface {
  docCount: Int!
  key: String!
  name: String!
  stats: Stats!
}

input TermsAggregation {
  field: String!
  order: String
  size: Int
}

type TermsAggregationResult implements AggregationResultInterface {
  docCount: Int!
  key: String!
}
sigdestad commented 3 years ago

Looks promising. Is the node stuff a utility for testing things?

sigdestad commented 3 years ago

I'm thinkint that maybe a few of the fields should have more specific values? I.e. dateRange from/to are strings. Is this specific enough, or should it validate ISO date format for instance?

anatol-sialitski commented 3 years ago

Looks promising. Is the node stuff a utility for testing things?

I used Node JS in order to generate and validate GraphQL schema.

anatol-sialitski commented 3 years ago

Hi @sigdestad, @alansemenov

I've updated the schema as discussed on the meeting, please have a look

input AggregationInput {
  name: String!
  terms: TermsAggregation
  stats: StatsAggregation
  range: RangeAggregation
  geoDistance: GeoDistanceAggregation
  dateRange: DateRangeAggregation
  dateHistogram: DateHistogramAggregation
  subAggregations: [AggregationInput]
}

input BooleanFilter {
  must: [FilterInput]
  notMust: [FilterInput]
  should: [FilterInput]
}

interface Content {
  _id: ID!
  _name: String!
  _path: String!
  _references: [Content]
  displayName: String
}

input DateHistogramAggregation {
  field: String!
  interval: String
  format: String
  minDocCount: Int
}

input DateRange {
  from: String
  to: String
}

input DateRangeAggregation {
  field: String!
  format: String
  ranges: [DateRange]
  range: DateRange
}

input ExistsFilter {
  field: String!
}

input FilterInput {
  boolean: BooleanFilter
  exists: ExistsFilter
  notExists: NotExistsFilter
  hasValue: HasValueFilter
  ids: IdsFilter
}

input GeoDistanceAggregation {
  field: String!
  ranges: [Range]
  range: Range
  unit: String
  origin: GeoPoint
}

input GeoPoint {
  lat: Float!
  lon: Float!
}

input HasValueFilter {
  field: String!
  stringValues: [String]
  intValues: [Int]
  floatValues: [Float]
  booleanValues: [Boolean]
}

input IdsFilter {
  values: [String]!
}

input NotExistsFilter {
  field: String!
}

type Query {
  query(aggregations: [AggregationInput], filters: [FilterInput]): QueryResult
}

type QueryResult {
  hits: [Content]
  aggregationsAsJson: String
}

input Range {
  from: Float
  to: Float
}

input RangeAggregation {
  field: String!
  ranges: [Range]
  range: Range
}

input StatsAggregation {
  field: String!
}

input TermsAggregation {
  field: String!
  order: String
  size: Int
}

Usage

query  {
  query(aggregations: [{
    name: "products",
    terms: {
      field: "data.product.category",
      order: "_count desc",
      size: 10
    }  
    subAggregations: [{
      name: "priceStats",
      stats: {
        field: "data.product.price"
      }
    }]
 }]) {
    aggregationsAsJson
  }
}