Je lis les données d'un déclencheur Update Cloud Firestore. L ' événement
est un dictionnaire qui contient les données contenues dans la clé [' value '] [' fields ']
. Cependant, chacune des clés contient un dictionnaire imbriqué contenant une clé comme 'integerValue'
, 'booleanValue'
ou 'stringValue'
, où la valeur de integerValue
est en fait une chaîne. Existe-t-il une méthode pour supprimer les "pointeurs de type"?
Comment puis-je convertir ceci:
{ 'count': 0, 'verified': False, 'user': 'Matt', }
En ceci:
{ 'fields': { 'count': { 'integerValue': '0' }, 'verified': { 'booleanValue': False }, 'user': { 'stringValue': 'Matt' } } }
4 Réponses :
Il n'y a pas de méthode explicite pour le faire. Une chose que vous pouvez faire est de parcourir le dictionnaire existant en récupérant les éléments dont vous avez besoin dans le nouveau dictionnaire:
d = { 'fields': { 'count': { 'integerValue': '0' }, 'verified': { 'booleanValue': False }, 'user': { 'stringValue': 'Matt' } } } required = ['count', 'verified', 'user'] d1 = {} for x in d.values(): for y in required: if 'integerValue' in x[y].keys(): d1[y] = int(list(x[y].values())[0]) else: d1[y] = list(x[y].values())[0] print(d1) # {'count': 0, 'verified': False, 'user': 'Matt'}
Austin Je vois, je l'ai fait quand je testais une fonction et je n'ai pas vraiment remarqué la integerValue
jusqu'à ce que mes documents deviennent un peu plus complexes. Il me semble que la meilleure façon de gérer cela dans google-cloud-functions
pour python est d'obtenir les données directement de Firestore lors du déclenchement de la fonction au lieu d'essayer de l'analyser comme vous et @Bob avez suggéré.
@Racu, peut-être que ça ferait l'affaire. Nous n'avions aucune idée de l'historique de votre travail, et vous n'en avez pas non plus mentionné davantage dans votre question. De plus, je dois admettre que je n'ai aucune expérience de travail avec Firestore.
Utilisez les touches () dans le dictionnaire
origin_dict={ 'fields': { 'count': { 'integerValue': '0' }, 'verified': { 'booleanValue': False }, 'user': { 'stringValue': 'Matt' } } } # remove first layer b = origin_dict['fields'] new_dict = dict() for i in b.keys(): # i will be second layer of dictionary for j in b[i].keys(): # j will be third layer of dictionary new_dict[i] = b[i][j] print (new_dict)
Peut être simplifié en utilisant for i in b:
et for j in b [i]:
.
Vous pouvez créer un mappage des types connus et convertir les valeurs de cette façon:
event['value'] = {k: types[t](v) for k t, v in (k, *d.popitem()) for k, d in event['value']['fields'].items())}
Vous pouvez remplacer un dictionnaire imbriqué comme celui que vous avez grâce à la magie de dict.popitem
:
replacement = {} for key, meta in event['value']['fields'].items(): typ, value = meta.popitem() replacement[key] = types[typ](value) event['value'] = replacement
Vous pouvez le réduire à une seule ligne avec une compréhension du dictionnaire:
types = { 'integerValue': int, 'booleanValue': bool, 'stringValue': str, }
Récemment, j'ai rencontré un problème similaire.
Nous pourrions parcourir la carte de manière récursive pour extraire et simplifier les données du déclencheur d'événements.
Voici l'implémentation python, étendue à partir des réponses précédentes.
converter = FirestoreTriggerConverter(client)
simplified_data_dict = converter.convert(event_data_dict["event"]["value"]["fields"])
Utilisez ceci comme suit
class FirestoreTriggerConverter(object): def __init__(self, client=None) -> None: self.client = client if client else firestore.client() self._action_dict = { 'geoPointValue': (lambda x: dict(x)), 'stringValue': (lambda x: str(x)), 'arrayValue': (lambda x: [self._parse_value(value_dict) for value_dict in x["values"]]), 'booleanValue': (lambda x: bool(x)), 'nullValue': (lambda x: None), 'timestampValue': (lambda x: self._parse_timestamp(x)), 'referenceValue': (lambda x: self._parse_doc_ref(x)), 'mapValue': (lambda x: {key: self._parse_value(value) for key, value in x["fields"].items()}), 'integerValue': (lambda x: int(x)), 'doubleValue': (lambda x: float(x)), } def convert(self, data_dict: dict) -> dict: result_dict = {} for key, value_dict in data_dict.items(): result_dict[key] = self._parse_value(value_dict) return result_dict def _parse_value(self, value_dict: dict) -> Any: data_type, value = value_dict.popitem() return self._action_dict[data_type](value) def _parse_timestamp(self, timestamp: str): try: return datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%fZ') except ValueError as e: return datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%SZ') def _parse_doc_ref(self, doc_ref: str) -> DocumentReference: path_parts = doc_ref.split('/documents/')[1].split('/') collection_path = path_parts[0] document_path = '/'.join(path_parts[1:]) doc_ref = self.client.collection(collection_path).document(document_path) return doc_ref
Avez-vous fait une tentative?
@MadPhysicist Pas vraiment parce que je suis nouveau avec python, j'apprends au fur et à mesure. J'ai vérifié les méthodes possibles pour un dictionnaire. Mais ne semble pas trouver quoi que ce soit qui le rend plus simple. J'ai utilisé ce que «Austin» et «Bob» ont mentionné dans les réponses avec un événement plus petit avec un champ, jusqu'à ce que je trouve des dictionnaires plus gros. Avec les fonctions JavaScript, c'est plus facile, vous obtenez simplement l'accès aux données en tant que propriétés. On dirait que je devrai récupérer les données directement à partir du firestore lors du déclenchement de l'événement au lieu d'essayer de les lire à partir de l'événement.