|
2 | 2 |
|
3 | 3 |
|
4 | 4 | def render_query(dataset, tables, select=None, conditions=None, |
5 | | - groupings=None, order_by=None): |
| 5 | + groupings=None, having=None, order_by=None): |
6 | 6 | """Render a query that will run over the given tables using the specified |
7 | 7 | parameters. |
8 | 8 |
|
@@ -46,12 +46,13 @@ def render_query(dataset, tables, select=None, conditions=None, |
46 | 46 | if None in (dataset, tables): |
47 | 47 | return None |
48 | 48 |
|
49 | | - query = "%s %s %s %s %s" % ( |
| 49 | + query = "%s %s %s %s %s %s" % ( |
50 | 50 | _render_select(select), |
51 | 51 | _render_sources(dataset, tables), |
52 | 52 | _render_conditions(conditions), |
53 | 53 | _render_groupings(groupings), |
54 | | - _render_order(order_by), |
| 54 | + _render_having(having), |
| 55 | + _render_order(order_by) |
55 | 56 | ) |
56 | 57 |
|
57 | 58 | return query |
@@ -133,8 +134,21 @@ def _render_sources(dataset, tables): |
133 | 134 | a string that represents the from part of a query. |
134 | 135 | """ |
135 | 136 |
|
136 | | - return "FROM " + ", ".join( |
137 | | - ["[%s.%s]" % (dataset, table) for table in tables]) |
| 137 | + if isinstance(tables, dict): |
| 138 | + if tables.get('date_range', False): |
| 139 | + try: |
| 140 | + dataset_table = '.'.join([dataset, tables['table']]) |
| 141 | + return "FROM (TABLE_DATE_RANGE([{}], TIMESTAMP('{}'),"\ |
| 142 | + " TIMESTAMP('{}'))) ".format(dataset_table, |
| 143 | + tables['from_date'], |
| 144 | + tables['to_date']) |
| 145 | + except KeyError as exp: |
| 146 | + logging.warn('Missing parameter %s in selecting sources' % |
| 147 | + (exp)) |
| 148 | + |
| 149 | + else: |
| 150 | + return "FROM " + ", ".join( |
| 151 | + ["[%s.%s]" % (dataset, table) for table in tables]) |
138 | 152 |
|
139 | 153 |
|
140 | 154 | def _render_conditions(conditions): |
@@ -206,6 +220,15 @@ def _render_condition(field, field_type, comparators): |
206 | 220 | else: |
207 | 221 | value = _render_condition_value(value, field_type) |
208 | 222 | value = "(" + value + ")" |
| 223 | + elif condition == "BETWEEN": |
| 224 | + if isinstance(value, (tuple, list, set)) and len(value) == 2: |
| 225 | + value = ' AND '.join( |
| 226 | + sorted([_render_condition_value(v, field_type) |
| 227 | + for v in value]) |
| 228 | + ) |
| 229 | + elif isinstance(value, (tuple, list, set)) and len(value) != 2: |
| 230 | + logging.warn('Invalid condition passed in: %s' % condition) |
| 231 | + |
209 | 232 | else: |
210 | 233 | value = _render_condition_value(value, field_type) |
211 | 234 |
|
@@ -242,38 +265,76 @@ def _render_condition_value(value, field_type): |
242 | 265 | value = 1 if value else 0 |
243 | 266 | elif field_type in ("STRING", "INTEGER", "FLOAT"): |
244 | 267 | value = "'%s'" % (value) |
| 268 | + elif field_type in ("TIMESTAMP"): |
| 269 | + value = "'%s'" % (str(value)) |
245 | 270 | return "%s(%s)" % (field_type, value) |
246 | 271 |
|
247 | 272 |
|
248 | | -def _render_order(order): |
249 | | - """Render the order by part of a query. |
| 273 | +def _render_groupings(fields): |
| 274 | + """Render the group by part of a query. |
250 | 275 |
|
251 | 276 | Args: |
252 | | - order: a dictionary with two keys, field and direction. |
253 | | - Such that the dictionary should be formatted as |
254 | | - {'field':'TimeStamp, 'direction':'desc'}. |
| 277 | + fields: a list of fields to group by. |
255 | 278 |
|
256 | 279 | Returns: |
257 | | - a string that represents the order by part of a query. |
| 280 | + a string that represents the group by part of a query. |
258 | 281 | """ |
259 | 282 |
|
260 | | - if not order or 'field' not in order or 'direction' not in order: |
261 | | - return '' |
| 283 | + if not fields: |
| 284 | + return "" |
262 | 285 |
|
263 | | - return "ORDER BY %s %s" % (order['field'], order['direction']) |
| 286 | + return "GROUP BY " + ", ".join(fields) |
264 | 287 |
|
265 | 288 |
|
266 | | -def _render_groupings(fields): |
267 | | - """Render the group by part of a query. |
| 289 | +def _render_having(having_conditions): |
| 290 | + """Render the having part of a query. |
268 | 291 |
|
269 | 292 | Args: |
270 | | - fields: a list of fields to group by. |
| 293 | + conditions: a list of dictionary items to filter the rows. |
| 294 | + Each dict should be formatted as {'field': 'start_time', |
| 295 | + 'value': {'value': 1, 'negate': False}, 'comparator': '>', |
| 296 | + 'type': 'FLOAT'} which is represetned as |
| 297 | + 'start_time > FLOAT('1')' in the query. |
271 | 298 |
|
272 | 299 | Returns: |
273 | | - a string that represents the group by part of a query. |
| 300 | + a string that represents the having part of a query. |
274 | 301 | """ |
| 302 | + if not having_conditions: |
| 303 | + return "" |
275 | 304 |
|
276 | | - if not fields: |
| 305 | + rendered_conditions = [] |
| 306 | + |
| 307 | + for condition in having_conditions: |
| 308 | + field = condition.get('field') |
| 309 | + field_type = condition.get('type') |
| 310 | + comparators = condition.get('comparators') |
| 311 | + |
| 312 | + if None in (field, field_type, comparators) or not comparators: |
| 313 | + logging.warn('Invalid condition passed in: %s' % condition) |
| 314 | + continue |
| 315 | + |
| 316 | + rendered_conditions.append( |
| 317 | + _render_condition(field, field_type, comparators)) |
| 318 | + |
| 319 | + if not rendered_conditions: |
277 | 320 | return "" |
278 | 321 |
|
279 | | - return "GROUP BY " + ", ".join(fields) |
| 322 | + return "HAVING %s" % (" AND ".join(rendered_conditions)) |
| 323 | + |
| 324 | + |
| 325 | +def _render_order(order): |
| 326 | + """Render the order by part of a query. |
| 327 | +
|
| 328 | + Args: |
| 329 | + order: a dictionary with two keys, fields and direction. |
| 330 | + Such that the dictionary should be formatted as |
| 331 | + {'fields': ['TimeStamp'], 'direction':'desc'}. |
| 332 | +
|
| 333 | + Returns: |
| 334 | + a string that represents the order by part of a query. |
| 335 | + """ |
| 336 | + |
| 337 | + if not order or 'fields' not in order or 'direction' not in order: |
| 338 | + return '' |
| 339 | + |
| 340 | + return "ORDER BY %s %s" % (", ".join(order['fields']), order['direction']) |
0 commit comments