По мере того, как мы переходим к моделям документов в наших базах данных, важно иметь хорошие инструменты, позволяющие адекватно отфильтровывать ненужную и конфиденциальную информацию.
В этой статье мы будем использовать библиотеку flatten-dict, доступную по адресу https://github.com/ianlini/flatten-dict и устанавливаемую через pip install flatten-dict
.
Пример документа, который нужно отфильтровать:
user = { "name": "Bob Robertson", "classification": { "kingdom": "animalia", "phylum": "chordata", "class": "mammalia", "order": "primates", "suborder": "haplorhini", "infraorder": "simiiformes", "family": "hominidae", "subfamily": "homininae", "tribe": "hominini", "genus": "homo", "species": "h. sapiens", }, "statistics": { "dimensions": { "height_cm": 194.2, "weight_kg": 96.52, }, "digits": { "toes": 10, "fingers": 10, } }, }
Давайте представим, что большая часть этой информации используется серверной службой, но не используется панелью управления, представляющей пользователя. Мы хотим отфильтровать все содержимое, кроме выделенного жирным шрифтом: имя, классификационные виды и включать только параметры из статистики.
Первое, что нам нужно сделать, это свести словарь к списку с точечной записью. Это предполагает, что все ключи в документе еще не содержат точки (.
).
from flatten_dict import flatten flattened_user_doc = flatten(user_doc, reducer="dot")
В результате содержимое flattened_user_doc
is:
{ "name": "Bob Robertson", "classification.kingdom": "animalia", "classification.phylum": "chordata", "classification.class": "mammalia", "classification.order": "primates", "classification.suborder": "haplorhini", "classification.infraorder": "simiiformes", "classification.family": "hominidae", "classification.subfamily": "homininae", "classification.tribe": "hominini", "classification.genus": "homo", "classification.species": "h. sapiens", "statistics.dimensions.height_cm": 194.2, "statistics.dimensions.weight_kg": 96.52, "statistics.digits.toes": 10, "statistics.digits.fingers": 10 }
OK. Достаточно просто. Теперь давайте воспользуемся регулярными выражениями и списком сопоставителей, чтобы действовать как фильтр, а затем отфильтровать результат.
import re filters = [ re.compile("^name$"), re.compile("^classification.species$"), re.compile("^statistics.dimensions"), ] filtered_flattened_user_doc = { k: v for k, v in flattened_user_doc.items() if [True for f in filters if f.match(k)] } from flatten_dict import unflatten filtered_user_doc = unflatten(filtered_flattened_user_doc, splitter="dot")
В результате содержимое filtered_flattened_user_doc
теперь выглядит следующим образом:
{ "name": "Bob Robertson", "classification.species": "h. sapiens", "statistics.dimensions.height_cm": 194.2, "statistics.dimensions.weight_kg": 96.52 }
И наконец у нас есть filtered_user_doc
:
{ "name": "Bob Robertson", "classification": { "species": "h. sapiens" }, "statistics": { "dimensions": { "height_cm": 194.2, "weight_kg": 96.52 } } }
Я считаю, что есть еще несколько модулей, которые помогают выполнять эту фильтрацию. Использование flatten
и unflatten
из модуля flatten-dict и встроенного регулярного выражения просто и понятно.
Необязательно использовать регулярные выражения. Я использовал их, чтобы сопоставить пути с частичными точечными обозначениями.
Вам не обязательно использовать точки. Вы можете использовать кортежи с flatten_dict, а также список кортежей для сопоставления.
В настоящее время я использую этот код для фильтрации объединенных документов из информации об аккаунте, пользователе и устройстве пользователя, полученной из различных источников. Использование явной фильтрации позволяет мне немного меньше беспокоиться о том, что передается пользователю, поскольку я вношу изменения в документы и модели во время разработки и текущего обслуживания моего проекта.