# SPDX-License-Identifier: Apache-2.0 # # The OpenSearch Contributors require contributions made to # this file be licensed under the Apache-2.0 license or a # compatible open source license. # # Modifications Copyright OpenSearch Contributors. See # GitHub history for details. # # Licensed to Elasticsearch B.V. under one or more contributor # license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright # ownership. Elasticsearch B.V. licenses this file to you 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. require_relative '../test_helper' module OpenSearch module Test class AggregationsIntegrationTest < ::OpenSearch::Test::IntegrationTestCase include OpenSearch::DSL::Search context 'Aggregations integration' do setup do @client.indices.create index: 'test', body: { mappings: { properties: { tags: { type: 'keyword' } } } } @client.index index: 'test', id: '1', body: { title: 'A', tags: %w[one], clicks: 5 } @client.index index: 'test', id: '2', body: { title: 'B', tags: %w[one two], clicks: 15 } @client.index index: 'test', id: '3', body: { title: 'C', tags: %w[one three], clicks: 20 } @client.indices.refresh index: 'test' end context 'with a terms aggregation' do should 'return tag counts' do response = @client.search index: 'test', body: search { aggregation :tags do terms field: 'tags' end }.to_hash assert_equal 3, response['aggregations']['tags']['buckets'].size assert_equal 'one', response['aggregations']['tags']['buckets'][0]['key'] end should 'return approximate tag counts' do response = @client.search index: 'test', body: search { aggregation :tags do cardinality field: 'tags' end }.to_hash assert_equal 3, response['aggregations']['tags']['value'] end should 'return tag counts per clicks range as an inner (nested) aggregation' do response = @client.search index: 'test', body: search { aggregation :clicks do range field: 'clicks' do key :low, to: 10 key :mid, from: 10, to: 20 aggregation :tags do terms field: 'tags' end end end }.to_hash assert_equal 2, response['aggregations']['clicks']['buckets'].size assert_equal 1, response['aggregations']['clicks']['buckets']['low']['doc_count'] assert_equal 'one', response['aggregations']['clicks']['buckets']['low']['tags']['buckets'][0]['key'] end should 'define multiple aggregations' do response = @client.search index: 'test', body: search { aggregation :clicks do range field: 'clicks' do key :low, to: 10 key :mid, from: 10, to: 20 aggregation :tags do terms { field 'tags' } end end end aggregation :min_clicks do min field: 'clicks' end aggregation :max_clicks do max field: 'clicks' end aggregation :sum_clicks do sum field: 'clicks' end aggregation :avg_clicks do avg field: 'clicks' end }.to_hash assert_equal 2, response['aggregations']['clicks']['buckets'].size assert_equal 1, response['aggregations']['clicks']['buckets']['low']['doc_count'] assert_equal 'one', response['aggregations']['clicks']['buckets']['low']['tags']['buckets'][0]['key'] assert_equal 5, response['aggregations']['min_clicks']['value'] assert_equal 20, response['aggregations']['max_clicks']['value'] assert_equal 40, response['aggregations']['sum_clicks']['value'] assert_equal 13, response['aggregations']['avg_clicks']['value'].to_i end should 'define a global aggregation' do response = @client.search index: 'test', body: search { query do bool filter: { terms: { tags: ['two'] } } end aggregation :avg_clicks do avg field: 'clicks' end aggregation :all_documents do global do aggregation :avg_clicks do avg field: 'clicks' end end end }.to_hash assert_equal 15, response['aggregations']['avg_clicks']['value'].to_i assert_equal 13, response['aggregations']['all_documents']['avg_clicks']['value'].to_i end should 'return statistics on clicks' do response = @client.search index: 'test', body: search { aggregation :stats_clicks do stats field: 'clicks' end aggregation :value_count do value_count field: 'clicks' end }.to_hash assert_equal 3, response['aggregations']['stats_clicks']['count'] assert_equal 5, response['aggregations']['stats_clicks']['min'] assert_equal 20, response['aggregations']['stats_clicks']['max'] assert_equal 40, response['aggregations']['stats_clicks']['sum'] assert_equal 13, response['aggregations']['stats_clicks']['avg'].to_i assert_equal 3, response['aggregations']['value_count']['value'] end should 'return percentiles on clicks' do response = @client.search index: 'test', body: search { aggregation :percentiles do percentiles field: 'clicks' end }.to_hash assert_equal 20, response['aggregations']['percentiles']['values']['99.0'].round end should 'return percentile ranks on clicks' do response = @client.search index: 'test', body: search { aggregation :percentiles do percentile_ranks field: 'clicks', values: [5] end }.to_hash assert_equal 17, response['aggregations']['percentiles']['values']['5.0'].round end should 'return top hits per tag' do response = @client.search index: 'test', body: search { aggregation :tags do terms do field 'tags' size 5 aggregation :top_hits do top_hits sort: [clicks: { order: 'desc' }], _source: { include: 'title' } end end end }.to_hash assert_equal 3, response['aggregations']['tags']['buckets'][0]['top_hits']['hits']['hits'].size assert_equal 'C', response['aggregations']['tags']['buckets'][0]['top_hits']['hits']['hits'][0]['_source']['title'] end should 'calculate clicks for a tag' do response = @client.search index: 'test', body: search { aggregation :clicks_for_one do scripted_metric do init_script 'state.transactions = []' map_script "if (doc['tags'].value.contains('one')) { state.transactions.add(doc['clicks'].value) }" combine_script 'double sum = 0; for (t in state.transactions) { sum += t } return sum' reduce_script 'double sum = 0; for (a in states) { sum += a } return sum' end end }.to_hash assert_equal 40, response['aggregations']['clicks_for_one']['value'] end should 'limit the scope with a filter' do response = @client.search index: 'test', body: search { aggregation :clicks_for_one do filter terms: { tags: ['one'] } do aggregation :sum_clicks do sum field: 'clicks' end end end }.to_hash assert_equal 40, response['aggregations']['clicks_for_one']['sum_clicks']['value'] end end should 'return aggregations for multiple filters' do response = @client.search index: 'test', body: search { aggregation :avg_clicks_per_tag do filters do filters one: { terms: { tags: ['one'] } }, two: { terms: { tags: ['two'] } } aggregation :avg do avg field: 'clicks' end end end }.to_hash assert_equal 13, response['aggregations']['avg_clicks_per_tag']['buckets']['one']['avg']['value'].to_i assert_equal 15, response['aggregations']['avg_clicks_per_tag']['buckets']['two']['avg']['value'].to_i end should 'return a histogram on clicks' do response = @client.search index: 'test', body: search { aggregation :clicks_histogram do histogram do field 'clicks' interval 10 end end }.to_hash assert_equal 3, response['aggregations']['clicks_histogram']['buckets'].size assert_equal 10, response['aggregations']['clicks_histogram']['buckets'][1]['key'] assert_equal 1, response['aggregations']['clicks_histogram']['buckets'][1]['doc_count'] end should 'return a histogram with empty buckets on clicks' do response = @client.search index: 'test', body: search { aggregation :clicks_histogram do histogram do field 'clicks' interval 2 min_doc_count 0 end end }.to_hash assert_equal 9, response['aggregations']['clicks_histogram']['buckets'].size end end end end end