def doc(self, collection: str, oid: Union[ObjectId, str], query: Union[list, None], reference_delete=True) -> dict: """ | refもしくはembのドキュメントを取得する | オプションでedman特有のデータ含んで取得することもできる :param str collection: :param oid: :type oid: ObjectId or str :param query: :type query: list or None :param bool reference_delete: default True :return: result :rtype: dict """ oid = Utils.conv_objectid(oid) doc = self.db[collection].find_one({'_id': oid}) if doc is None: sys.exit('ドキュメントが存在しません') # embの場合は指定階層のドキュメントを引き抜く # refの場合はdocの結果をそのまま入れる doc_result = self._get_emb_doc(doc, query) if query is not None else doc # クエリの指定によってはリストデータなども取得出てしまうため if not isinstance(doc_result, dict): sys.exit(f'指定されたクエリはドキュメントではありません {query}') result = Utils.reference_item_delete( doc_result, ('_id', self.parent, self.child, self.file_ref) ) if reference_delete else doc_result return result
def get_file_names(self, collection: str, oid: Union[ObjectId, str], structure: str, query: Union[list, None]) -> dict: """ ファイル一覧を取得 :param str collection: :param str oid: :param str structure: :param query: embの時だけ必要. refの時はNone :type query: list or None :return: result :rtype: dict """ oid = Utils.conv_objectid(oid) # ドキュメント存在確認&コレクション存在確認&対象ドキュメント取得 doc = self.db[collection].find_one({'_id': oid}) if doc is None: sys.exit('対象のコレクション、またはドキュメントが存在しません') # ファイルリスト取得 files_list = self.get_file_ref(doc, structure, query) result = {} if files_list: # ファイルリストを元にgridfsからファイル名を取り出す for file_oid in files_list: fs_out = self.fs.get(file_oid) result.update({file_oid: fs_out.filename}) else: sys.exit('関連ファイルはありません') return result
def test_conv_objectid(self): # 正常系 oidの場合 oid = ObjectId() actual = Utils.conv_objectid(oid) self.assertIsInstance(actual, ObjectId) self.assertEqual(oid, actual) # 正常系 文字列の場合 oid = ObjectId() actual = Utils.conv_objectid(str(oid)) self.assertIsInstance(actual, ObjectId) self.assertEqual(oid, actual) # 異常系 oidにならない文字列 oid = str(ObjectId()) oid = oid[:-1] with self.assertRaises((SystemExit, errors.InvalidId)) as cm: _ = Utils.conv_objectid(oid)
def delete(self, oid: Union[str, ObjectId], collection: str, structure: str) -> bool: """ | ドキュメントを削除する | 指定のoidを含む下位のドキュメントを全削除 | refで親が存在する時は親のchildリストから指定のoidを取り除く :param oid: :type oid: str or ObjectId :param str collection: :param str structure: :return: :rtype: bool """ oid = Utils.conv_objectid(oid) db_result = self.db[collection].find_one({'_id': oid}) if db_result is None: sys.exit('該当するドキュメントは存在しません') if structure == 'emb': try: result = self.db[collection].delete_one({'_id': oid}) if result.deleted_count: # 添付データがあればgridfsから削除 file = File(self.get_db) file.fs_delete( sum([i for i in self._collect_emb_file_ref( db_result, self.file_ref)], [])) return True else: sys.exit('指定のドキュメントは削除できませんでした' + str(oid)) except ValueError as e: sys.exit(e) elif structure == 'ref': try: # 親ドキュメントがあれば子要素リストから削除する if db_result.get(self.parent): self._delete_reference_from_parent(db_result[self.parent], db_result['_id']) # 対象のドキュメント以下のドキュメントと関連ファイルを削除する self._delete_documents_and_files(db_result, collection) return True except ValueError as e: sys.exit(e) else: sys.exit('structureはrefまたはembの指定が必要です')
def get_structure(self, collection: str, oid: ObjectId) -> str: """ 対象のドキュメントの構造を取得する :param str collection: :param ObjectId oid: :return: ref or emb :rtype: str """ doc = self.db[collection].find_one({'_id': Utils.conv_objectid(oid)}) if doc is None: sys.exit('指定のドキュメントがありません') if any(key in doc for key in (self.parent, self.child)): return 'ref' else: return 'emb'
def update(self, collection: str, oid: Union[str, ObjectId], amend_data: dict, structure: str) -> bool: """ 修正データを用いてDBデータをアップデート :param str collection: :param oid: :type oid: str or ObjectId :param dict amend_data: :param str structure: :return: :rtype: bool """ oid = Utils.conv_objectid(oid) db_result = self.db[collection].find_one({'_id': oid}) if db_result is None: sys.exit('該当するドキュメントは存在しません') if structure == 'emb': try: # 日付データを日付オブジェクトに変換するため、 # 必ずコンバートしてからマージする convert = Convert() converted_amend_data = convert.emb(amend_data) amended = self._merge(db_result, converted_amend_data) except ValueError as e: sys.exit(e) elif structure == 'ref': # 日付データを日付オブジェクトに変換 converted_amend_data = self._convert_datetime_dict(amend_data) amended = {**db_result, **converted_amend_data} else: sys.exit('structureはrefまたはembの指定が必要です') try: replace_result = self.db[collection].replace_one({'_id': oid}, amended) except errors.OperationFailure: sys.exit('アップデートに失敗しました') return True if replace_result.modified_count == 1 else False
def find_collection_from_objectid(self, oid: Union[str, ObjectId]) -> Union[str, None]: """ | DB内のコレクションから指定のObjectIDを探し、所属しているコレクションを返す | DBに負荷がかかるので使用は注意が必要 :param oid: :type oid: ObjectId or str :return: collection :rtype: str or None """ oid = Utils.conv_objectid(oid) result = None coll_filter = {"name": {"$regex": r"^(?!system\.)"}} for collection in self.db.list_collection_names(filter=coll_filter): find_oid = self.db[collection].find_one({'_id': oid}) if find_oid is not None and '_id' in find_oid: result = collection break return result
def item_delete(self, collection: str, oid: Union[ObjectId, str], delete_key: str, query: Union[list, None]) -> bool: """ ドキュメントの項目を削除する :param str collection: :param oid: :type oid: str or ObjectId :param str delete_key: :param query: :type query: list or None :return: :rtype: bool """ oid = Utils.conv_objectid(oid) doc = self.db[collection].find_one({'_id': oid}) if doc is None: sys.exit('ドキュメントが存在しません') if query is not None: # emb try: doc = Utils.doc_traverse(doc, [delete_key], query, self._delete_execute) except Exception as e: sys.exit(e) else: # ref try: del doc[delete_key] except IndexError: sys.exit(f'キーは存在しません: {delete_key}') # ドキュメント置き換え処理 replace_result = self.db[collection].replace_one({'_id': oid}, doc) result = True if replace_result.modified_count == 1 else False return result
def structure(self, collection: str, oid: ObjectId, structure_mode: str, new_collection: str) -> list: """ 構造をrefからembへ、またはembからrefへ変更する :param str collection: :param ObjectId oid: :param str structure_mode: :param str new_collection: :return: structured_result :rtype: list """ oid = Utils.conv_objectid(oid) # refデータをembに変換する if structure_mode == 'emb': # 自分データ取り出し ref_result = self.doc(collection, oid, query=None, reference_delete=False) reference_point_result = self.get_reference_point( ref_result) if reference_point_result[self.child]: # 子データを取り出し children = self.get_child_all({collection: ref_result}) # 自分のリファレンスデータとidを削除 for del_key in (self.parent, self.child, '_id'): if del_key in ref_result: del ref_result[del_key] # 子のリファレンスデータ削除 non_ref_children = self.delete_reference(children, ('_id', self.parent, self.child)) # 自分と子要素をマージする ref_result.update(non_ref_children) convert = Convert() converted_edman = convert.dict_to_edman( {new_collection: ref_result}, mode='emb') structured_result = self.insert(converted_edman) # 子が存在しないドキュメントの場合(新たなコレクションとして切り出す) else: # 自分のリファレンスデータとidを削除 for del_key in (self.parent, '_id'): if del_key in ref_result: del ref_result[del_key] convert = Convert() converted_edman = convert.dict_to_edman( {new_collection: ref_result}, mode='emb') structured_result = self.insert(converted_edman) # embからrefに変換 elif structure_mode == 'ref': emb_result = self.db[collection].find_one({'_id': oid}) del emb_result['_id'] convert = Convert() converted_edman = convert.dict_to_edman( {new_collection: emb_result}, mode='ref') structured_result = self.insert(converted_edman) structured_result.reverse() else: sys.exit('構造はrefかembを指定してください') return structured_result
def add_file_reference(self, collection: str, oid: Union[ObjectId, str], file_path: Tuple[Path], structure: str, query=None, compress=False) -> bool: """ ドキュメントにファイルリファレンスを追加する ファイルのインサート処理、圧縮処理なども行う :param str collection: :param oid: :type oid: ObjectId or str :param tuple file_path: :param str structure: :param query: :type query: list or None :param bool compress: default False :return: :rtype: bool """ oid = Utils.conv_objectid(oid) # ドキュメント存在確認&対象ドキュメント取得 doc = self.db[collection].find_one({'_id': oid}) if doc is None: sys.exit('対象のドキュメントが存在しません') if structure == 'emb': # クエリーがドキュメントのキーとして存在するかチェック if not Utils.query_check(query, doc): sys.exit('対象のドキュメントに対してクエリーが一致しません.') # ファイルのインサート inserted_file_oids = [] for file in self.file_gen(file_path): file_obj = file[1] metadata = {'filename': file[0]} if compress: file_obj = gzip.compress(file_obj, compresslevel=6) metadata.update({'compress': 'gzip'}) inserted_file_oids.append(self.fs.put(file_obj, **metadata)) if structure == 'ref': new_doc = self._file_list_attachment(doc, inserted_file_oids) elif structure == 'emb': try: new_doc = Utils.doc_traverse(doc, inserted_file_oids, query, self._file_list_attachment) except Exception as e: sys.exit(e) else: sys.exit('構造はrefかembが必要です') # ドキュメント差し替え replace_result = self.db[collection].replace_one({'_id': oid}, new_doc) if replace_result.modified_count == 1: return True else: # 差し替えができなかった時は添付ファイルは削除 self.fs_delete(inserted_file_oids) return False
def delete(self, delete_oid: ObjectId, collection: str, oid: Union[ObjectId, str], structure: str, query=None) -> bool: """ 該当のoidをファイルリファレンスから削除し、GridFSからファイルを削除 :param ObjectId delete_oid: :param str collection: :param str oid: :param str structure: :param query: :type query: list or None :return: :rtype: bool """ oid = Utils.conv_objectid(oid) # ドキュメント存在確認&コレクション存在確認&対象ドキュメント取得 doc = self.db[collection].find_one({'_id': oid}) if doc is None: sys.exit('対象のコレクション、またはドキュメントが存在しません') # ファイルリスト取得 files_list = self.get_file_ref(doc, structure, query) # リファレンスデータを編集 if len(files_list) > 0: # 何らかの原因で重複があった場合を避けるため一度setにする files_list = list(set(files_list)) files_list.remove(delete_oid) else: sys.exit('ファイルが存在しません') # ドキュメントを新しいファイルリファレンスに置き換える if structure == 'ref': try: new_doc = self._file_list_replace(doc, files_list) except Exception as e: sys.exit(e) elif structure == 'emb': try: new_doc = Utils.doc_traverse(doc, files_list, query, self._file_list_replace) except Exception as e: sys.exit(e) else: sys.exit('structureはrefまたはembの指定が必要です') replace_result = self.db[collection].replace_one({'_id': oid}, new_doc) # fsから該当ファイルを削除 if replace_result.modified_count: self.fs_delete([delete_oid]) # ファイルが削除されたか検証 if self.fs.exists(delete_oid): return False else: return True