tellaro-query-language 0.1.5__py3-none-any.whl → 0.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tellaro_query_language-0.1.5.dist-info → tellaro_query_language-0.1.7.dist-info}/METADATA +1 -1
- {tellaro_query_language-0.1.5.dist-info → tellaro_query_language-0.1.7.dist-info}/RECORD +9 -9
- tql/core_components/opensearch_operations.py +78 -34
- tql/core_components/stats_operations.py +2 -1
- tql/opensearch_stats.py +10 -0
- tql/parser_components/grammar.py +3 -1
- {tellaro_query_language-0.1.5.dist-info → tellaro_query_language-0.1.7.dist-info}/LICENSE +0 -0
- {tellaro_query_language-0.1.5.dist-info → tellaro_query_language-0.1.7.dist-info}/WHEEL +0 -0
- {tellaro_query_language-0.1.5.dist-info → tellaro_query_language-0.1.7.dist-info}/entry_points.txt +0 -0
|
@@ -8,8 +8,8 @@ tql/core.py,sha256=zAkTStN_3logY9ARABDZNsQJfXV5rITiiTKoX5bo274,40859
|
|
|
8
8
|
tql/core_components/README.md,sha256=Rm7w4UHdQ0vPBEFybE5b62IOvSA5Nzq2GRvtBHOapmc,3068
|
|
9
9
|
tql/core_components/__init__.py,sha256=v8BBybPlqV7dkVY9mw1mblvqyAFJZ7Pf_bEc-jAL7FI,643
|
|
10
10
|
tql/core_components/file_operations.py,sha256=Jr0kkxz_OP2KHOAsIr7KMtYe_lbu8LuBUySt2LQbjJw,3925
|
|
11
|
-
tql/core_components/opensearch_operations.py,sha256=
|
|
12
|
-
tql/core_components/stats_operations.py,sha256=
|
|
11
|
+
tql/core_components/opensearch_operations.py,sha256=X9xtVnrC37O-9eSTbdilvi_cjvm0jXHZIPdldM0yM7g,38028
|
|
12
|
+
tql/core_components/stats_operations.py,sha256=zAfDhVOFFPMrRIMw6Qtjxbobbdi7ao_HuHBCcVc3BGY,7579
|
|
13
13
|
tql/core_components/validation_operations.py,sha256=_VPXh0HABBjsXF99jFT7B6-5QAPsADOCy6poinGrxeE,22454
|
|
14
14
|
tql/evaluator.py,sha256=YdgS1vuxUEPAHhUsZey-Y4NydeS8CTYOy_O8R5_K8cE,15421
|
|
15
15
|
tql/evaluator_components/README.md,sha256=c59yf2au34yPhrru7JWgGop_ORteB6w5vfMhsac8j3k,3882
|
|
@@ -36,21 +36,21 @@ tql/opensearch_components/field_mapping.py,sha256=N4r7VkzNeXjIhNDt2cfnd1LbkvaS_9
|
|
|
36
36
|
tql/opensearch_components/lucene_converter.py,sha256=ZbupWZ2smGhWfE9cSIrNo7MZVY35l2t86HYM-bd7nKw,12436
|
|
37
37
|
tql/opensearch_components/query_converter.py,sha256=9Nkhehb8X7jIQcyRaHCa5WCKWtovdRGKuSdDsBulrG0,36722
|
|
38
38
|
tql/opensearch_mappings.py,sha256=zJCCdMrxK7mswrkxd5LiOhunQ9GIJNZdhktVoGXgVgk,11529
|
|
39
|
-
tql/opensearch_stats.py,sha256=
|
|
39
|
+
tql/opensearch_stats.py,sha256=03mjvnUJpNtCDpU-E1ZkugDJMIKC-F76Hekd4gppPxU,17812
|
|
40
40
|
tql/parser.py,sha256=dnjgc-sDVihe-VIVPT_SRULeng4OWaLtkbM23dluT6M,75532
|
|
41
41
|
tql/parser_components/README.md,sha256=lvQX72ckq2zyotGs8QIHHCIFqaA7bOHwkP44wU8Zoiw,2322
|
|
42
42
|
tql/parser_components/__init__.py,sha256=zBwHBMPJyHSBbaOojf6qTrJYjJg5A6tPUE8nHFdRiQs,521
|
|
43
43
|
tql/parser_components/ast_builder.py,sha256=-pbcYhZNoRm0AnjmJRAAlXLCAwHfauchTpX_6KO0plE,6793
|
|
44
44
|
tql/parser_components/error_analyzer.py,sha256=qlCD9vKyW73aeKQYI33P1OjIWSJ3LPd08wuN9cis2fU,4012
|
|
45
45
|
tql/parser_components/field_extractor.py,sha256=TumeuUo2c5gPYVbTPsmU43C3TJFC8chAAWERu5v_Q3c,4182
|
|
46
|
-
tql/parser_components/grammar.py,sha256=
|
|
46
|
+
tql/parser_components/grammar.py,sha256=lSvjABvEBaH29-ad-_UGD4WmofdNwC_pO2OKQJ_It-U,19309
|
|
47
47
|
tql/post_processor.py,sha256=-vA2wgbuLij2FVnj5I9HDHtw5bKj9Cu3EE9mtoeSWk8,28859
|
|
48
48
|
tql/scripts.py,sha256=VOr5vCjIvKlW36kwvJx7JGFIRM16IJZlbJcWlBexBtk,3728
|
|
49
49
|
tql/stats_evaluator.py,sha256=GtmygLdPPMhAMR5bN1h69cQsNBYqg-jQl6bN5rwEx6Q,15692
|
|
50
50
|
tql/stats_transformer.py,sha256=MT-4rDWZSySgn4Fuq9H0c-mvwFYLM6FqWpPv2rHX-rE,7588
|
|
51
51
|
tql/validators.py,sha256=e9MlX-zQ_O3M8YP8vXyMjKU8iiJMTh6mMK0iv0_4gTY,3771
|
|
52
|
-
tellaro_query_language-0.1.
|
|
53
|
-
tellaro_query_language-0.1.
|
|
54
|
-
tellaro_query_language-0.1.
|
|
55
|
-
tellaro_query_language-0.1.
|
|
56
|
-
tellaro_query_language-0.1.
|
|
52
|
+
tellaro_query_language-0.1.7.dist-info/LICENSE,sha256=zRhQ85LnW55fWgAjQctckwQ67DX5Jmt64lq343ThZFU,1063
|
|
53
|
+
tellaro_query_language-0.1.7.dist-info/METADATA,sha256=IDzVPrFm_GQJTRZRX4iLNw45Nqszf3TqBW6ZW6UUKBo,15109
|
|
54
|
+
tellaro_query_language-0.1.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
55
|
+
tellaro_query_language-0.1.7.dist-info/entry_points.txt,sha256=H43APfGBMsZkKsUCnFTaqprQPW-Kce2yz2qsBL3dZrw,164
|
|
56
|
+
tellaro_query_language-0.1.7.dist-info/RECORD,,
|
|
@@ -441,49 +441,93 @@ class OpenSearchOperations:
|
|
|
441
441
|
fields = [agg.get("field") for agg in aggregations]
|
|
442
442
|
|
|
443
443
|
stats_results = {
|
|
444
|
-
"type": "
|
|
444
|
+
"type": "stats_grouped",
|
|
445
445
|
"operation": operations[0] if len(operations) == 1 else operations,
|
|
446
446
|
"field": fields[0] if len(fields) == 1 else fields,
|
|
447
|
-
"
|
|
447
|
+
"results": buckets, # Array of buckets for grouped results
|
|
448
448
|
"group_by": group_by_fields,
|
|
449
449
|
}
|
|
450
450
|
else:
|
|
451
451
|
# Simple aggregations without grouping
|
|
452
452
|
if aggregations:
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
453
|
+
if len(aggregations) == 1:
|
|
454
|
+
# Single aggregation
|
|
455
|
+
first_agg = aggregations[0]
|
|
456
|
+
func = first_agg.get("function", "")
|
|
457
|
+
field = first_agg.get("field", "*")
|
|
458
|
+
|
|
459
|
+
# Get the aggregation result
|
|
460
|
+
# The alias is typically func_field_0 for the first aggregation
|
|
461
|
+
alias = first_agg.get("alias") or f"{func}_{field}_0"
|
|
462
|
+
agg_result = aggs_response.get(alias, {})
|
|
463
|
+
|
|
464
|
+
# Extract the value based on aggregation type
|
|
465
|
+
if func == "count":
|
|
466
|
+
value = agg_result.get("value", 0)
|
|
467
|
+
elif func in ["sum", "min", "max", "avg", "average"]:
|
|
468
|
+
value = agg_result.get("value", 0)
|
|
469
|
+
elif func == "unique_count":
|
|
470
|
+
value = agg_result.get("value", 0)
|
|
471
|
+
elif func in ["percentile", "percentiles", "p", "pct"]:
|
|
472
|
+
# Percentiles return a values dict
|
|
473
|
+
values_dict = agg_result.get("values", {})
|
|
474
|
+
# For a single percentile, extract the value
|
|
475
|
+
if len(values_dict) == 1:
|
|
476
|
+
value = list(values_dict.values())[0]
|
|
477
|
+
else:
|
|
478
|
+
value = values_dict
|
|
479
|
+
elif func in ["values", "unique"]:
|
|
480
|
+
# Extract buckets from terms aggregation
|
|
481
|
+
buckets = agg_result.get("buckets", [])
|
|
482
|
+
value = [bucket["key"] for bucket in buckets]
|
|
475
483
|
else:
|
|
476
|
-
value =
|
|
484
|
+
value = agg_result
|
|
485
|
+
|
|
486
|
+
stats_results = {
|
|
487
|
+
"type": "stats",
|
|
488
|
+
"operation": func,
|
|
489
|
+
"field": field,
|
|
490
|
+
"values": value,
|
|
491
|
+
"group_by": [],
|
|
492
|
+
}
|
|
477
493
|
else:
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
494
|
+
# Multiple aggregations
|
|
495
|
+
results = {}
|
|
496
|
+
for i, agg in enumerate(aggregations):
|
|
497
|
+
func = agg.get("function", "")
|
|
498
|
+
field = agg.get("field", "*")
|
|
499
|
+
alias = agg.get("alias") or f"{func}_{field}_{i}"
|
|
500
|
+
agg_result = aggs_response.get(alias, {})
|
|
501
|
+
|
|
502
|
+
# Extract the value based on aggregation type
|
|
503
|
+
if func == "count":
|
|
504
|
+
value = agg_result.get("value", 0)
|
|
505
|
+
elif func in ["sum", "min", "max", "avg", "average"]:
|
|
506
|
+
value = agg_result.get("value", 0)
|
|
507
|
+
elif func == "unique_count":
|
|
508
|
+
value = agg_result.get("value", 0)
|
|
509
|
+
elif func in ["percentile", "percentiles", "p", "pct"]:
|
|
510
|
+
# Percentiles return a values dict
|
|
511
|
+
values_dict = agg_result.get("values", {})
|
|
512
|
+
# For a single percentile, extract the value
|
|
513
|
+
if len(values_dict) == 1:
|
|
514
|
+
value = list(values_dict.values())[0]
|
|
515
|
+
else:
|
|
516
|
+
value = values_dict
|
|
517
|
+
elif func in ["values", "unique"]:
|
|
518
|
+
# Extract buckets from terms aggregation
|
|
519
|
+
buckets = agg_result.get("buckets", [])
|
|
520
|
+
value = [bucket["key"] for bucket in buckets]
|
|
521
|
+
else:
|
|
522
|
+
value = agg_result
|
|
523
|
+
|
|
524
|
+
key = agg.get("alias") or f"{func}_{field}"
|
|
525
|
+
results[key] = value
|
|
526
|
+
|
|
527
|
+
stats_results = {
|
|
528
|
+
"type": "stats",
|
|
529
|
+
"results": results,
|
|
530
|
+
}
|
|
487
531
|
else:
|
|
488
532
|
stats_results = {"type": "stats", "operation": "unknown", "field": "*", "values": 0, "group_by": []}
|
|
489
533
|
|
|
@@ -133,7 +133,8 @@ class StatsOperations:
|
|
|
133
133
|
"""
|
|
134
134
|
# Parse the query
|
|
135
135
|
try:
|
|
136
|
-
if
|
|
136
|
+
# Don't add prefix if query already starts with "stats"
|
|
137
|
+
if not query.strip().startswith("| stats") and not query.strip().startswith("stats") and "|" not in query:
|
|
137
138
|
query = "| stats " + query.strip()
|
|
138
139
|
|
|
139
140
|
ast = self.parser.parse(query)
|
tql/opensearch_stats.py
CHANGED
|
@@ -33,6 +33,9 @@ class OpenSearchStatsTranslator:
|
|
|
33
33
|
"pct_rank": "percentile_ranks",
|
|
34
34
|
"pct_ranks": "percentile_ranks",
|
|
35
35
|
"zscore": None, # Requires post-processing
|
|
36
|
+
"values": "terms", # Return unique values
|
|
37
|
+
"unique": "terms", # Alias for values
|
|
38
|
+
"cardinality": "cardinality", # Count of unique values
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
# Aggregations that require numeric fields
|
|
@@ -146,6 +149,9 @@ class OpenSearchStatsTranslator:
|
|
|
146
149
|
if not rank_values:
|
|
147
150
|
raise TQLError("percentile_rank requires at least one value")
|
|
148
151
|
aggs_dsl[alias] = {"percentile_ranks": {"field": field, "values": rank_values}}
|
|
152
|
+
elif func in ["values", "unique"]:
|
|
153
|
+
# Terms aggregation to get unique values
|
|
154
|
+
aggs_dsl[alias] = {"terms": {"field": field, "size": 10000}} # Large size to get all values
|
|
149
155
|
else:
|
|
150
156
|
# Direct mapping
|
|
151
157
|
aggs_dsl[alias] = {os_agg_type: {"field": field}}
|
|
@@ -447,5 +453,9 @@ class OpenSearchStatsTranslator:
|
|
|
447
453
|
for k, v in values.items():
|
|
448
454
|
result[f"rank_{k}"] = v
|
|
449
455
|
return result
|
|
456
|
+
elif function in ["values", "unique"]:
|
|
457
|
+
# Extract buckets from terms aggregation
|
|
458
|
+
buckets = agg_result.get("buckets", [])
|
|
459
|
+
return [bucket["key"] for bucket in buckets]
|
|
450
460
|
else:
|
|
451
461
|
return None
|
tql/parser_components/grammar.py
CHANGED
|
@@ -380,8 +380,9 @@ class TQLGrammar:
|
|
|
380
380
|
caseless=True,
|
|
381
381
|
)
|
|
382
382
|
|
|
383
|
-
# Special case for count(*)
|
|
383
|
+
# Special case for count(*) and count()
|
|
384
384
|
self.count_all = CaselessKeyword("count") + Suppress("(") + Suppress("*") + Suppress(")")
|
|
385
|
+
self.count_empty = CaselessKeyword("count") + Suppress("(") + Suppress(")")
|
|
385
386
|
|
|
386
387
|
# Aggregation function with field
|
|
387
388
|
self.agg_function = (
|
|
@@ -395,6 +396,7 @@ class TQLGrammar:
|
|
|
395
396
|
+ Suppress(")")
|
|
396
397
|
)
|
|
397
398
|
| self.count_all
|
|
399
|
+
| self.count_empty
|
|
398
400
|
)
|
|
399
401
|
|
|
400
402
|
# Support for aliasing: sum(revenue) as total_revenue
|
|
File without changes
|
|
File without changes
|
{tellaro_query_language-0.1.5.dist-info → tellaro_query_language-0.1.7.dist-info}/entry_points.txt
RENAMED
|
File without changes
|