|
1 | 1 | from __future__ import annotations
|
2 | 2 | import datetime
|
| 3 | +import re |
3 | 4 | from enum import Enum
|
4 | 5 | from abc import ABC
|
5 | 6 | from typing import Union, Optional, List, Dict, Set, Iterable
|
@@ -301,7 +302,7 @@ def detect_static_index_source_type(self) -> IndexSourceType:
|
301 | 302 |
|
302 | 303 | source_type = IndexSourceType.NONE
|
303 | 304 | for map in self.maps:
|
304 |
| - map_source_type = None # todo: IndexDefinitionHelper.detect_static_index_source_type(map) |
| 305 | + map_source_type = IndexDefinitionHelper.detect_static_index_source_type(map) |
305 | 306 | if source_type == IndexSourceType.NONE:
|
306 | 307 | source_type = map_source_type
|
307 | 308 | continue
|
@@ -574,3 +575,69 @@ def __init__(self, name: Optional[str] = None, errors: Optional[List[IndexingErr
|
574 | 575 | @classmethod
|
575 | 576 | def from_json(cls, json_dict: dict) -> IndexErrors:
|
576 | 577 | return cls(json_dict["Name"], list(map(lambda x: IndexingError.from_json(x), json_dict["Errors"])))
|
| 578 | + |
| 579 | + |
| 580 | +class IndexDefinitionHelper: |
| 581 | + @staticmethod |
| 582 | + def detect_static_index_type(map_str: str, reduce: str) -> IndexType: |
| 583 | + if len(map_str) == 0: |
| 584 | + raise ValueError("Index definitions contains no maps") |
| 585 | + |
| 586 | + map_str = IndexDefinitionHelper._strip_comments(map_str) |
| 587 | + map_str = IndexDefinitionHelper._unify_white_space(map_str) |
| 588 | + |
| 589 | + map_lower = map_str.lower() |
| 590 | + if ( |
| 591 | + map_lower.startswith("from") |
| 592 | + or map_lower.startswith("docs") |
| 593 | + or (map_lower.startswith("timeseries") and not map_lower.startswith("timeseries.map")) |
| 594 | + or (map_lower.startswith("counters") and not map_lower.startswith("counters.map")) |
| 595 | + ): |
| 596 | + # C# indexes must start with "from" query syntax or |
| 597 | + # "docs" for method syntax |
| 598 | + if reduce is None or reduce.isspace(): |
| 599 | + return IndexType.MAP |
| 600 | + return IndexType.MAP_REDUCE |
| 601 | + |
| 602 | + if reduce.isspace(): |
| 603 | + return IndexType.JAVA_SCRIPT_MAP |
| 604 | + |
| 605 | + return IndexType.JAVA_SCRIPT_MAP_REDUCE |
| 606 | + |
| 607 | + @staticmethod |
| 608 | + def detect_static_index_source_type(map_str: str) -> IndexSourceType: |
| 609 | + if not map_str or map_str.isspace(): |
| 610 | + raise ValueError("Value cannot be None or whitespace") |
| 611 | + |
| 612 | + map_str = IndexDefinitionHelper._strip_comments(map_str) |
| 613 | + map_str = IndexDefinitionHelper._unify_white_space(map_str) |
| 614 | + |
| 615 | + # detect first supported syntax: timeseries.Companies.HeartRate.Where |
| 616 | + map_lower = map_str.lower() |
| 617 | + if map_lower.startswith("timeseries"): |
| 618 | + return IndexSourceType.TIME_SERIES |
| 619 | + |
| 620 | + if map_lower.startswith("counters"): |
| 621 | + return IndexSourceType.COUNTERS |
| 622 | + |
| 623 | + if map_lower.startswith("from"): |
| 624 | + # detect 'from ts in timeseries' or 'from ts in timeseries.Users.HeartRate' |
| 625 | + |
| 626 | + tokens = [token for token in map_lower.split(" ", 4) if token] |
| 627 | + |
| 628 | + if len(tokens) > 4 and tokens[2].lower() == "in": |
| 629 | + if tokens[3].startswith("timeseries"): |
| 630 | + return IndexSourceType.TIME_SERIES |
| 631 | + if tokens[3].startswith("counters"): |
| 632 | + return IndexSourceType.COUNTERS |
| 633 | + |
| 634 | + # fallback to documents based index |
| 635 | + return IndexSourceType.DOCUMENTS |
| 636 | + |
| 637 | + @staticmethod |
| 638 | + def _strip_comments(map_str: str) -> str: |
| 639 | + return re.sub("(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*)", "", map_str).strip() |
| 640 | + |
| 641 | + @staticmethod |
| 642 | + def _unify_white_space(map_str: str) -> str: |
| 643 | + return re.sub("\\s+", " ", map_str) |
0 commit comments