Exemple #1
0
    def batch_get(self,
                  data_table: DataTable[_RecordType],
                  consistent_read: bool = False) -> DataTable[_RecordType]:
        """
        Get multuple records as a DataTable from DB.

        `data_table` must have all columns to calculate table keys.

        Example:

            ```python
            # UserTable is a subclass of a DynamoTable
            user_table = UserTable()

            # we should provide table keys or fields to calculate them
            # in our case, PK is calculated from `email` field.
            users_table = DataTable[UserRecord]().add_record(
                {
                    "email": "*****@*****.**",
                },
                {
                    "email": "*****@*****.**",
                },
            )
            user_records = user_table.batch_get(users_table)

            for user_record in user_records:
                # print found records
                # if record was not found - it will still be returned
                # but only with the data you provided
                print(user_record)
            ```

        Arguments:
            data_table -- Request data table.
            consistent_read -- `ConsistentRead` boto3 parameter.

        Returns:
            DataTable with existing records.
        """

        if not data_table:
            return data_table.copy()
        get_data_table = DataTable()
        for record in data_table.get_records():
            record = self._convert_record(record)
            record = self.normalize_record(record)
            record.update(self._get_record_keys(record))
            get_data_table.add_record(record)

        results: DataTable[Any] = (
            self.dynamo_query_class.build_batch_get_item(
                consistent_read=consistent_read, logger=self._logger).table(
                    table_keys=self.table_keys,
                    table=self.table).execute(data_table=get_data_table))
        return DataTable(record_class=self.record_class).add_table(results)
Exemple #2
0
    def cached_batch_get(
            self,
            data_table: DataTable[_RecordType]) -> DataTable[_RecordType]:
        """
        Get multuple records as a DataTable from DB with caching.

        `data_table` must have all columns to calculate table keys.

        Can be used instead of `batch_get` method.

        Arguments:
            data_table -- Request data table.

        Returns:
            DataTable with existing records.
        """
        if not data_table:
            return data_table.copy()

        result = DataTable(record_class=self.record_class)
        non_cached_data_table = DataTable(record_class=self.record_class)
        cached_data_table = DataTable(record_class=self.record_class)
        for record in data_table.get_records():
            record = self._convert_record(record)
            record = self.normalize_record(record)
            record_keys = self._get_record_keys(record)
            cached_record = self._get_cached_record(record_keys)
            if cached_record is self.NO_RECORD:
                non_cached_data_table.add_record(record)
                continue

            if cached_record and not isinstance(cached_record, SentinelValue):
                cached_data_table.add_record(cached_record)

        non_cached_results = self.batch_get(non_cached_data_table)
        for record in non_cached_results:
            record_keys = self._get_record_keys(record)
            self._cache_record(record_keys, record)
        result.add_table(cached_data_table)
        result.add_table(non_cached_results)
        return result
    def test_add_record() -> None:
        data_table = DataTable({"a": [1], "b": [3]})
        result = data_table.add_record({"a": 5, "c": 4}, {"c": 5})
        assert result is data_table
        assert data_table == {
            "a": [1, 5, data_table.NOT_SET],
            "b": [3, data_table.NOT_SET, data_table.NOT_SET],
            "c": [data_table.NOT_SET, 4, 5],
        }

        with pytest.raises(DataTableError):
            DataTable({"a": [1], "b": []}).add_record({"a": 1})
    def test_custom_record(self):
        data_table = DataTable(record_class=UserRecord)
        data_table.add_record({"name": "Jon"})
        data_table.add_record(UserRecord(name="test", age=12))
        assert isinstance(data_table.get_record(0), UserRecord)
        assert list(data_table.get_records()) == [
            UserRecord(name="Jon"),
            UserRecord(name="test", age=12),
        ]

        with pytest.raises(ValueError):
            data_table.add_record({"unknown": "Jon"})

        with pytest.raises(ValueError):
            data_table.add_record({})
def main() -> None:
    users_table = DataTable(record_class=UserRecord)
    users_table.add_record(
        {
            "email": "*****@*****.**",
            "name": "John",
            "age": 34
        },
        {
            "email": "*****@*****.**",
            "company": "CiscoSystems",
            "name": "Mary",
            "age": 34
        },
    )

    print("Get John's record:")
    print(users_table.get_record(0))

    print("All records as a list:")
    print(list(users_table.get_records()))

    print("Find record with name=Mary:")
    print(users_table.filter_records({"name": "Mary"}).get_record(0))
Exemple #6
0
    def batch_upsert(
        self,
        data_table: DataTable[_RecordType],
        set_if_not_exists_keys: Iterable[str] = (),
    ) -> DataTable[_RecordType]:
        """
        Upsert multuple records as a DataTable to DB.

        `data_table` must have all columns to calculate table keys.

        Sets `dt_created` field equal to current UTC datetime if a record was created.
        Sets `dt_modified` field equal to current UTC datetime.

        Example:

            ```python
            # UserTable is a subclass of a DynamoTable
            user_table = UserTable()

            # we should provide table keys or fields to calculate them
            # in our case, PK is calculated from `email` field.
            users_table = DataTable[UserRecord]().add_record(
                {
                    "email": "*****@*****.**",
                    "name": "Doge Barky",
                    "age": 20,
                },
                {
                    "email": "*****@*****.**",
                    "name": "Elon Musk",
                    "age": 5289,
                },
            )
            upserted_records = user_table.batch_upsert(users_table)

            for upserted_record in upserted_records:
                # print created and updated records
                print(upserted_record)
            ```

        Arguments:
            data_table -- Request DataTable.
            set_if_not_exists_keys -- List of keys to set only if they no do exist in DB.

        Returns:
            A DataTable with upserted results.
        """
        if not data_table:
            return DataTable[_RecordType](record_class=self.record_class)

        set_if_not_exists = set(set_if_not_exists_keys)
        existing_records = self.batch_get(data_table)
        now = datetime.datetime.utcnow()
        now_str = now.isoformat()

        update_data_table = DataTable(record_class=self.record_class)
        for record_index, record in enumerate(existing_records.get_records()):
            updated_record = self._convert_record(
                data_table.get_record(record_index))
            preserve_keys_record: Dict[str, Any] = {}
            for key in set_if_not_exists:
                if key in record:
                    preserve_keys_record[key] = record[key]

            new_record = self._convert_record({
                **record,
                **updated_record,
                **preserve_keys_record,
                "dt_created":
                record.get("dt_created") or now_str,
                "dt_modified":
                now_str,
            })

            normalized_record = self.normalize_record(new_record)
            self.validate_record_attributes(normalized_record)
            update_data_table.add_record(dict(normalized_record))

        results: DataTable[Any] = (
            self.dynamo_query_class.build_batch_update_item(
                logger=self._logger).table(
                    table_keys=self.table_keys,
                    table=self.table,
                ).execute(update_data_table))
        results.record_class = self.record_class
        return results