1mod agg_limits;
131pub mod agg_req;
132mod agg_req_with_accessor;
133pub mod agg_result;
134pub mod bucket;
135mod buf_collector;
136mod collector;
137mod date;
138mod error;
139pub mod intermediate_agg_result;
140pub mod metric;
141
142mod segment_agg_result;
143use std::collections::HashMap;
144use std::fmt::Display;
145
146#[cfg(test)]
147mod agg_tests;
148
149use core::fmt;
150
151pub use agg_limits::AggregationLimitsGuard;
152pub use collector::{
153 AggregationCollector, AggregationSegmentCollector, DistributedAggregationCollector,
154 DEFAULT_BUCKET_LIMIT,
155};
156use columnar::{ColumnType, MonotonicallyMappableToU64};
157pub(crate) use date::format_date;
158pub use error::AggregationError;
159use itertools::Itertools;
160use serde::de::{self, Visitor};
161use serde::{Deserialize, Deserializer, Serialize};
162
163fn parse_str_into_f64<E: de::Error>(value: &str) -> Result<f64, E> {
164 let parsed = value
165 .parse::<f64>()
166 .map_err(|_err| de::Error::custom(format!("Failed to parse f64 from string: {value:?}")))?;
167
168 if parsed.is_nan() || parsed.is_infinite() {
170 Err(de::Error::custom(format!(
171 "Value is not a valid f64 (NaN or Infinity): {value:?}"
172 )))
173 } else {
174 Ok(parsed)
175 }
176}
177
178pub(crate) fn deserialize_option_f64<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
180where D: Deserializer<'de> {
181 struct StringOrFloatVisitor;
182
183 impl Visitor<'_> for StringOrFloatVisitor {
184 type Value = Option<f64>;
185
186 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
187 formatter.write_str("a string or a float")
188 }
189
190 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
191 where E: de::Error {
192 parse_str_into_f64(value).map(Some)
193 }
194
195 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
196 where E: de::Error {
197 Ok(Some(value))
198 }
199
200 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
201 where E: de::Error {
202 Ok(Some(value as f64))
203 }
204
205 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
206 where E: de::Error {
207 Ok(Some(value as f64))
208 }
209
210 fn visit_none<E>(self) -> Result<Self::Value, E>
211 where E: de::Error {
212 Ok(None)
213 }
214
215 fn visit_unit<E>(self) -> Result<Self::Value, E>
216 where E: de::Error {
217 Ok(None)
218 }
219 }
220
221 deserializer.deserialize_any(StringOrFloatVisitor)
222}
223
224pub(crate) fn deserialize_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
226where D: Deserializer<'de> {
227 struct StringOrFloatVisitor;
228
229 impl Visitor<'_> for StringOrFloatVisitor {
230 type Value = f64;
231
232 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
233 formatter.write_str("a string or a float")
234 }
235
236 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
237 where E: de::Error {
238 parse_str_into_f64(value)
239 }
240
241 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
242 where E: de::Error {
243 Ok(value)
244 }
245
246 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
247 where E: de::Error {
248 Ok(value as f64)
249 }
250
251 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
252 where E: de::Error {
253 Ok(value as f64)
254 }
255 }
256
257 deserializer.deserialize_any(StringOrFloatVisitor)
258}
259
260#[derive(PartialEq, Serialize, Deserialize)]
262pub(crate) struct VecWithNames<T> {
263 pub(crate) values: Vec<T>,
264 keys: Vec<String>,
265}
266
267impl<T: Clone> Clone for VecWithNames<T> {
268 fn clone(&self) -> Self {
269 Self {
270 values: self.values.clone(),
271 keys: self.keys.clone(),
272 }
273 }
274}
275
276impl<T> Default for VecWithNames<T> {
277 fn default() -> Self {
278 Self {
279 values: Default::default(),
280 keys: Default::default(),
281 }
282 }
283}
284
285impl<T: std::fmt::Debug> std::fmt::Debug for VecWithNames<T> {
286 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287 f.debug_map().entries(self.iter()).finish()
288 }
289}
290
291impl<T> From<HashMap<String, T>> for VecWithNames<T> {
292 fn from(map: HashMap<String, T>) -> Self {
293 VecWithNames::from_entries(map.into_iter().collect_vec())
294 }
295}
296
297impl<T> VecWithNames<T> {
298 fn from_entries(mut entries: Vec<(String, T)>) -> Self {
299 entries.sort_by(|left, right| left.0.cmp(&right.0));
301 let mut data = Vec::with_capacity(entries.len());
302 let mut data_names = Vec::with_capacity(entries.len());
303 for entry in entries {
304 data_names.push(entry.0);
305 data.push(entry.1);
306 }
307 VecWithNames {
308 values: data,
309 keys: data_names,
310 }
311 }
312 fn iter(&self) -> impl Iterator<Item = (&str, &T)> + '_ {
313 self.keys().zip(self.values.iter())
314 }
315 fn keys(&self) -> impl Iterator<Item = &str> + '_ {
316 self.keys.iter().map(|key| key.as_str())
317 }
318 fn values_mut(&mut self) -> impl Iterator<Item = &mut T> + '_ {
319 self.values.iter_mut()
320 }
321 fn is_empty(&self) -> bool {
322 self.keys.is_empty()
323 }
324 fn len(&self) -> usize {
325 self.keys.len()
326 }
327 fn get(&self, name: &str) -> Option<&T> {
328 self.keys()
329 .position(|key| key == name)
330 .map(|pos| &self.values[pos])
331 }
332}
333
334pub type SerializedKey = String;
336
337#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd)]
338#[serde(untagged)]
342pub enum Key {
343 Str(String),
345 I64(i64),
347 U64(u64),
349 F64(f64),
351}
352impl Eq for Key {}
353impl std::hash::Hash for Key {
354 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
355 core::mem::discriminant(self).hash(state);
356 match self {
357 Key::Str(text) => text.hash(state),
358 Key::F64(val) => val.to_bits().hash(state),
359 Key::U64(val) => val.hash(state),
360 Key::I64(val) => val.hash(state),
361 }
362 }
363}
364
365impl PartialEq for Key {
366 fn eq(&self, other: &Self) -> bool {
367 match (self, other) {
368 (Self::Str(l), Self::Str(r)) => l == r,
369 (Self::F64(l), Self::F64(r)) => l.to_bits() == r.to_bits(),
370 (Self::I64(l), Self::I64(r)) => l == r,
371 (Self::U64(l), Self::U64(r)) => l == r,
372 (Self::Str(_) | Self::F64(_) | Self::I64(_) | Self::U64(_), _) => false,
375 }
376 }
377}
378
379impl Display for Key {
380 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
381 match self {
382 Key::Str(val) => f.write_str(val),
383 Key::F64(val) => f.write_str(&val.to_string()),
384 Key::U64(val) => f.write_str(&val.to_string()),
385 Key::I64(val) => f.write_str(&val.to_string()),
386 }
387 }
388}
389
390pub(crate) fn f64_from_fastfield_u64(val: u64, field_type: &ColumnType) -> f64 {
395 match field_type {
396 ColumnType::U64 => val as f64,
397 ColumnType::I64 | ColumnType::DateTime => i64::from_u64(val) as f64,
398 ColumnType::F64 => f64::from_u64(val),
399 ColumnType::Bool => val as f64,
400 _ => {
401 panic!("unexpected type {field_type:?}. This should not happen")
402 }
403 }
404}
405
406pub(crate) fn f64_to_fastfield_u64(val: f64, field_type: &ColumnType) -> Option<u64> {
416 match field_type {
417 ColumnType::U64 => Some(val as u64),
418 ColumnType::I64 | ColumnType::DateTime => Some((val as i64).to_u64()),
419 ColumnType::F64 => Some(val.to_u64()),
420 ColumnType::Bool => Some(val as u64),
421 _ => None,
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use std::net::Ipv6Addr;
428
429 use columnar::DateTime;
430 use serde_json::Value;
431 use time::OffsetDateTime;
432
433 use super::agg_req::Aggregations;
434 use super::*;
435 use crate::indexer::NoMergePolicy;
436 use crate::query::{AllQuery, TermQuery};
437 use crate::schema::{IndexRecordOption, Schema, TextFieldIndexing, FAST, STRING};
438 use crate::{Index, IndexWriter, Term};
439
440 pub fn get_test_index_with_num_docs(
441 merge_segments: bool,
442 num_docs: usize,
443 ) -> crate::Result<Index> {
444 get_test_index_from_values(
445 merge_segments,
446 &(0..num_docs).map(|el| el as f64).collect::<Vec<f64>>(),
447 )
448 }
449
450 pub fn exec_request(agg_req: Aggregations, index: &Index) -> crate::Result<Value> {
451 exec_request_with_query(agg_req, index, None)
452 }
453 pub fn exec_request_with_query(
454 agg_req: Aggregations,
455 index: &Index,
456 query: Option<(&str, &str)>,
457 ) -> crate::Result<Value> {
458 exec_request_with_query_and_memory_limit(agg_req, index, query, Default::default())
459 }
460
461 pub fn exec_request_with_query_and_memory_limit(
462 agg_req: Aggregations,
463 index: &Index,
464 query: Option<(&str, &str)>,
465 limits: AggregationLimitsGuard,
466 ) -> crate::Result<Value> {
467 let collector = AggregationCollector::from_aggs(agg_req, limits);
468
469 let reader = index.reader()?;
470 let searcher = reader.searcher();
471 let agg_res = if let Some((field, term)) = query {
472 let text_field = reader.searcher().schema().get_field(field).unwrap();
473
474 let term_query = TermQuery::new(
475 Term::from_field_text(text_field, term),
476 IndexRecordOption::Basic,
477 );
478
479 searcher.search(&term_query, &collector)?
480 } else {
481 searcher.search(&AllQuery, &collector)?
482 };
483
484 let res: Value = serde_json::from_str(&serde_json::to_string(&agg_res)?)?;
486 Ok(res)
487 }
488
489 pub fn get_test_index_from_values(
490 merge_segments: bool,
491 values: &[f64],
492 ) -> crate::Result<Index> {
493 let mut segment_and_values = vec![];
495 for value in values {
496 segment_and_values.push(vec![(*value, value.to_string())]);
497 }
498 get_test_index_from_values_and_terms(merge_segments, &segment_and_values)
499 }
500
501 pub fn get_test_index_from_terms(
502 merge_segments: bool,
503 values: &[Vec<&str>],
504 ) -> crate::Result<Index> {
505 let segment_and_values = values
507 .iter()
508 .map(|terms| {
509 terms
510 .iter()
511 .enumerate()
512 .map(|(i, term)| (i as f64, term.to_string()))
513 .collect()
514 })
515 .collect::<Vec<_>>();
516 get_test_index_from_values_and_terms(merge_segments, &segment_and_values)
517 }
518
519 pub fn get_test_index_from_values_and_terms(
520 merge_segments: bool,
521 segment_and_values: &[Vec<(f64, String)>],
522 ) -> crate::Result<Index> {
523 let mut schema_builder = Schema::builder();
524 let text_fieldtype = crate::schema::TextOptions::default()
525 .set_indexing_options(
526 TextFieldIndexing::default()
527 .set_index_option(IndexRecordOption::Basic)
528 .set_fieldnorms(false),
529 )
530 .set_fast(None)
531 .set_stored();
532 let text_field = schema_builder.add_text_field("text", text_fieldtype.clone());
533 let text_field_id = schema_builder.add_text_field("text_id", text_fieldtype);
534 let string_field_id = schema_builder.add_text_field("string_id", STRING | FAST);
535 let score_fieldtype = crate::schema::NumericOptions::default().set_fast();
536 let score_field = schema_builder.add_u64_field("score", score_fieldtype.clone());
537 let score_field_f64 = schema_builder.add_f64_field("score_f64", score_fieldtype.clone());
538 let score_field_i64 = schema_builder.add_i64_field("score_i64", score_fieldtype);
539 let fraction_field = schema_builder.add_f64_field(
540 "fraction_f64",
541 crate::schema::NumericOptions::default().set_fast(),
542 );
543 let index = Index::create_in_ram(schema_builder.build());
544 {
545 let mut index_writer = index.writer_with_num_threads(1, 20_000_000)?;
547 index_writer.set_merge_policy(Box::new(NoMergePolicy));
548 for values in segment_and_values {
549 for (i, term) in values {
550 let i = *i;
551 index_writer.add_document(doc!(
553 text_field => "cool",
554 text_field_id => term.to_string(),
555 string_field_id => term.to_string(),
556 score_field => i as u64,
557 score_field_f64 => i,
558 score_field_i64 => i as i64,
559 fraction_field => i/100.0,
560 ))?;
561 }
562 index_writer.commit()?;
563 }
564 }
565 if merge_segments {
566 let segment_ids = index
567 .searchable_segment_ids()
568 .expect("Searchable segments failed.");
569 if segment_ids.len() > 1 {
570 let mut index_writer: IndexWriter = index.writer_for_tests()?;
571 index_writer.merge(&segment_ids).wait()?;
572 index_writer.wait_merging_threads()?;
573 }
574 }
575
576 Ok(index)
577 }
578
579 pub fn get_test_index_2_segments(merge_segments: bool) -> crate::Result<Index> {
580 let mut schema_builder = Schema::builder();
581 let text_fieldtype = crate::schema::TextOptions::default()
582 .set_indexing_options(
583 TextFieldIndexing::default().set_index_option(IndexRecordOption::WithFreqs),
584 )
585 .set_fast(Some("raw"))
586 .set_stored();
587 let text_field = schema_builder.add_text_field("text", text_fieldtype);
588 let date_field = schema_builder.add_date_field("date", FAST);
589 schema_builder.add_text_field("dummy_text", STRING);
590 let score_fieldtype = crate::schema::NumericOptions::default().set_fast();
591 let score_field = schema_builder.add_u64_field("score", score_fieldtype.clone());
592 let score_field_f64 = schema_builder.add_f64_field("score_f64", score_fieldtype.clone());
593 let ip_addr_field = schema_builder.add_ip_addr_field("ip_addr", FAST);
594
595 let multivalue = crate::schema::NumericOptions::default().set_fast();
596 let scores_field_i64 = schema_builder.add_i64_field("scores_i64", multivalue);
597
598 let score_field_i64 = schema_builder.add_i64_field("score_i64", score_fieldtype);
599 let index = Index::create_in_ram(schema_builder.build());
600 {
601 let mut index_writer = index.writer_for_tests()?;
602 index_writer.add_document(doc!(
604 text_field => "cool",
605 date_field => DateTime::from_utc(OffsetDateTime::from_unix_timestamp(1_546_300_800).unwrap()),
606 score_field => 1u64,
607 ip_addr_field => Ipv6Addr::from(1u128),
608 score_field_f64 => 1f64,
609 score_field_i64 => 1i64,
610 scores_field_i64 => 1i64,
611 scores_field_i64 => 2i64,
612 ))?;
613 index_writer.add_document(doc!(
614 text_field => "cool",
615 date_field => DateTime::from_utc(OffsetDateTime::from_unix_timestamp(1_546_300_800 + 86400).unwrap()),
616 score_field => 3u64,
617 score_field_f64 => 3f64,
618 score_field_i64 => 3i64,
619 scores_field_i64 => 5i64,
620 scores_field_i64 => 5i64,
621 ))?;
622 index_writer.add_document(doc!(
623 text_field => "cool",
624 date_field => DateTime::from_utc(OffsetDateTime::from_unix_timestamp(1_546_300_800 + 86400).unwrap()),
625 score_field => 5u64,
626 score_field_f64 => 5f64,
627 score_field_i64 => 5i64,
628 ))?;
629 index_writer.add_document(doc!(
630 text_field => "nohit",
631 date_field => DateTime::from_utc(OffsetDateTime::from_unix_timestamp(1_546_300_800 + 86400).unwrap()),
632 score_field => 6u64,
633 score_field_f64 => 6f64,
634 score_field_i64 => 6i64,
635 ))?;
636 index_writer.add_document(doc!(
637 text_field => "cool",
638 date_field => DateTime::from_utc(OffsetDateTime::from_unix_timestamp(1_546_300_800 + 86400).unwrap()),
639 score_field => 7u64,
640 score_field_f64 => 7f64,
641 score_field_i64 => 7i64,
642 ))?;
643 index_writer.commit()?;
644 index_writer.add_document(doc!(
645 text_field => "cool",
646 date_field => DateTime::from_utc(OffsetDateTime::from_unix_timestamp(1_546_300_800 + 86400).unwrap()),
647 score_field => 11u64,
648 score_field_f64 => 11f64,
649 score_field_i64 => 11i64,
650 ))?;
651 index_writer.add_document(doc!(
652 text_field => "cool",
653 date_field => DateTime::from_utc(OffsetDateTime::from_unix_timestamp(1_546_300_800 + 86400 + 86400).unwrap()),
654 score_field => 14u64,
655 score_field_f64 => 14f64,
656 score_field_i64 => 14i64,
657 ))?;
658
659 index_writer.add_document(doc!(
660 text_field => "cool",
661 date_field => DateTime::from_utc(OffsetDateTime::from_unix_timestamp(1_546_300_800 + 86400 + 86400).unwrap()),
662 score_field => 44u64,
663 score_field_f64 => 44.5f64,
664 score_field_i64 => 44i64,
665 ))?;
666
667 index_writer.commit()?;
668
669 index_writer.add_document(doc!(
671 text_field => "nohit",
672 date_field => DateTime::from_utc(OffsetDateTime::from_unix_timestamp(1_546_300_800 + 86400 + 86400).unwrap()),
673 score_field => 44u64,
674 score_field_f64 => 44.5f64,
675 score_field_i64 => 44i64,
676 ))?;
677
678 index_writer.commit()?;
679 }
680 if merge_segments {
681 let segment_ids = index
682 .searchable_segment_ids()
683 .expect("Searchable segments failed.");
684 let mut index_writer: IndexWriter = index.writer_for_tests()?;
685 index_writer.merge(&segment_ids).wait()?;
686 index_writer.wait_merging_threads()?;
687 }
688
689 Ok(index)
690 }
691}