tellaro-query-language 0.1.2__py3-none-any.whl → 0.1.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tellaro-query-language
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: A flexible, human-friendly query language for searching and filtering structured data
5
5
  Home-page: https://github.com/tellaro/tellaro-query-language
6
6
  License: MIT
@@ -4,7 +4,7 @@ tql/cache/__init__.py,sha256=GIzIEMZUZEYJj72sAhuVLEG-OJEKUG2srUWNM3Ix-T8,213
4
4
  tql/cache/base.py,sha256=0b-8uyh3JltayGmXQI45snTqsM5sQu9u0KcNvZIRa-I,687
5
5
  tql/cache/memory.py,sha256=ibcmQSAxNvqCy6DksbU7gLu6UArYp1u3fW-oLubxtV0,2056
6
6
  tql/cache/redis.py,sha256=ZU_IsVDvpSYpNvPfnZ4iulJDODpEGx3c4dkXLzPzPVc,2309
7
- tql/core.py,sha256=HXz_95f9-yuBiI5iYGAPrpeqzokgSC5bsitOBLheuDU,37278
7
+ 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
@@ -49,8 +49,8 @@ tql/scripts.py,sha256=VOr5vCjIvKlW36kwvJx7JGFIRM16IJZlbJcWlBexBtk,3728
49
49
  tql/stats_evaluator.py,sha256=7ymCebPOepY3QKHGz0QEhkTmYYHjck648rrI8cWhhDI,15158
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.2.dist-info/LICENSE,sha256=zRhQ85LnW55fWgAjQctckwQ67DX5Jmt64lq343ThZFU,1063
53
- tellaro_query_language-0.1.2.dist-info/METADATA,sha256=nZ9Ib7w2FZTq4Nw3aJ20bEhyngtuoBMvuYAmfFXrRuw,15109
54
- tellaro_query_language-0.1.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
55
- tellaro_query_language-0.1.2.dist-info/entry_points.txt,sha256=H43APfGBMsZkKsUCnFTaqprQPW-Kce2yz2qsBL3dZrw,164
56
- tellaro_query_language-0.1.2.dist-info/RECORD,,
52
+ tellaro_query_language-0.1.4.dist-info/LICENSE,sha256=zRhQ85LnW55fWgAjQctckwQ67DX5Jmt64lq343ThZFU,1063
53
+ tellaro_query_language-0.1.4.dist-info/METADATA,sha256=vpS-ejEu2AU7SSXTML7q0x9jFwm3zZo13MwfLQxCPyI,15109
54
+ tellaro_query_language-0.1.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
55
+ tellaro_query_language-0.1.4.dist-info/entry_points.txt,sha256=H43APfGBMsZkKsUCnFTaqprQPW-Kce2yz2qsBL3dZrw,164
56
+ tellaro_query_language-0.1.4.dist-info/RECORD,,
tql/core.py CHANGED
@@ -12,6 +12,7 @@ from .evaluator import TQLEvaluator
12
12
  from .exceptions import TQLOperatorError, TQLParseError, TQLSyntaxError, TQLTypeError, TQLValidationError
13
13
  from .mutator_analyzer import MutatorAnalysisResult
14
14
  from .parser import TQLParser
15
+ from .stats_evaluator import TQLStatsEvaluator
15
16
 
16
17
 
17
18
  class TQL:
@@ -53,6 +54,7 @@ class TQL:
53
54
  """
54
55
  self.parser = TQLParser()
55
56
  self.evaluator = TQLEvaluator()
57
+ self.stats_evaluator = TQLStatsEvaluator()
56
58
  self.field_mappings = field_mappings or {}
57
59
 
58
60
  # Create enhanced field mappings for optimization
@@ -165,16 +167,25 @@ class TQL:
165
167
  return f"NOT {operand}"
166
168
  return str(ast)
167
169
 
168
- def query(self, data: Union[List[Dict], str], query: str, save_enrichment: bool = False) -> List[Dict[str, Any]]:
169
- """Execute a TQL query against data and return matching records.
170
+ def query(
171
+ self, data: Union[List[Dict], str], query: str, size: int = 10, save_enrichment: bool = False
172
+ ) -> Dict[str, Any]: # noqa: C901
173
+ """Execute a TQL query against data and return results in execute_opensearch format.
170
174
 
171
175
  Args:
172
176
  data: List of dictionaries or path to JSON/CSV file
173
- query: TQL query string
177
+ query: TQL query string (can be filter query, stats query, or combined)
178
+ size: Number of documents to return (0 for stats-only queries)
174
179
  save_enrichment: If True and mutators add enrichment fields, save back to source
175
180
 
176
181
  Returns:
177
- List of records matching the query
182
+ Dictionary containing:
183
+ - results: List of matching records (if size > 0 and no stats)
184
+ - stats: Stats results (if query contains stats)
185
+ - total: Total number of matching documents
186
+ - post_processing_applied: Whether post-processing was applied
187
+ - health_status: Query health status
188
+ - health_reasons: List of health issues
178
189
 
179
190
  Raises:
180
191
  TQLParseError: If query parsing fails
@@ -183,6 +194,9 @@ class TQL:
183
194
  # Parse the query
184
195
  ast = self.parse(query)
185
196
 
197
+ # Check query type
198
+ query_type = ast.get("type")
199
+
186
200
  # Load data if it's a file path
187
201
  if isinstance(data, str):
188
202
  records = self.file_ops.load_file(data)
@@ -191,23 +205,64 @@ class TQL:
191
205
  records = data
192
206
  source_file = None
193
207
 
194
- # Execute the query
195
- results = []
196
- has_enrichments = False
208
+ # Initialize result structure
209
+ result = {"total": 0, "post_processing_applied": False, "health_status": "green", "health_reasons": []}
197
210
 
198
- # Field mappings will be passed to evaluator methods
211
+ # Handle stats queries
212
+ if query_type == "stats_expr":
213
+ # This is a pure stats query like "| stats count()"
214
+ stats_result = self.stats(data, query)
215
+ # Convert to execute_opensearch format
216
+ result["stats"] = self._convert_stats_result(stats_result)
217
+ result["total"] = len(records)
218
+ return result
219
+
220
+ elif query_type == "query_with_stats":
221
+ # This is a combined query like "status = 'active' | stats count()"
222
+ # First filter the data
223
+ parsed = self.parse(query)
224
+ filter_ast = parsed["filter"]
225
+ stats_ast = parsed["stats"]
226
+
227
+ # Apply filter
228
+ filtered_records = []
229
+ for record in records:
230
+ if self.evaluator._evaluate_node(filter_ast, record, self._simple_mappings):
231
+ filtered_records.append(record)
232
+
233
+ # Apply stats to filtered data
234
+ stats_result = self.stats_evaluator.evaluate_stats(filtered_records, stats_ast)
235
+ result["stats"] = self._convert_stats_result(stats_result)
236
+ result["total"] = len(filtered_records)
237
+
238
+ # Include filtered documents if size > 0
239
+ if size > 0:
240
+ result["results"] = filtered_records[:size]
241
+
242
+ return result
243
+
244
+ # Handle regular filter queries
245
+ matched_records = []
246
+ has_enrichments = False
199
247
 
200
248
  for record in records:
201
249
  # Check if record matches
202
250
  if self.evaluator._evaluate_node(ast, record, self._simple_mappings):
203
251
  # Apply any mutators to enrich the record
204
252
  enriched_record = self._apply_mutators_to_record(ast, record)
205
- results.append(enriched_record)
253
+ matched_records.append(enriched_record)
206
254
  # Check if enrichments were added
207
255
  if not has_enrichments and enriched_record is not record:
208
256
  has_enrichments = True
209
257
 
210
- # No need to restore mappings since we pass them as parameters
258
+ # Set result data
259
+ result["total"] = len(matched_records)
260
+ if size > 0:
261
+ result["results"] = matched_records[:size]
262
+
263
+ # Check if post-processing (mutators) were applied
264
+ if has_enrichments:
265
+ result["post_processing_applied"] = True
211
266
 
212
267
  # Save enrichments if requested
213
268
  if save_enrichment and has_enrichments and source_file:
@@ -221,7 +276,7 @@ class TQL:
221
276
  if source_file.lower().endswith(".json"):
222
277
  self.file_ops.save_enrichments_to_json(source_file, all_enriched)
223
278
 
224
- return results
279
+ return result
225
280
 
226
281
  def query_single(self, record: Dict[str, Any], query: str) -> bool:
227
282
  """Check if a single record matches a TQL query.
@@ -927,3 +982,30 @@ class TQL:
927
982
  # For now, return the original record
928
983
  # TODO: Implement mutator application for enrichment # noqa: W0511
929
984
  return record
985
+
986
+ def _convert_stats_result(self, stats_result: Dict[str, Any]) -> Dict[str, Any]:
987
+ """Convert stats result from query() format to execute_opensearch format.
988
+
989
+ Args:
990
+ stats_result: Result from stats() or query_stats() method
991
+
992
+ Returns:
993
+ Stats result in execute_opensearch format
994
+ """
995
+ # Map the stats evaluator format to execute_opensearch format
996
+ if stats_result.get("type") == "simple_aggregation":
997
+ return {
998
+ "type": "stats",
999
+ "operation": stats_result["function"],
1000
+ "field": stats_result["field"],
1001
+ "values": stats_result["value"],
1002
+ }
1003
+ elif stats_result.get("type") == "multiple_aggregations":
1004
+ # For multiple aggregations, return the results dict
1005
+ return {"type": "stats_multiple", "results": stats_result["results"]}
1006
+ elif stats_result.get("type") == "grouped_aggregation":
1007
+ # For grouped aggregations
1008
+ return {"type": "stats_grouped", "group_by": stats_result["group_by"], "results": stats_result["results"]}
1009
+ else:
1010
+ # Return as-is if format is unknown
1011
+ return stats_result