blob: 6eb1f73fe9355ff537ef0e5f945acc0c2c2e0d21 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""
Entry representing a file.
"""
class FileEntry:
"""Entry representing a file.
The same Identifier indicates that the FileEntry refers to the same data file.
"""
class Identifier:
"""Unique identifier for a file entry.
Uses partition, bucket, level, fileName, extraFiles,
embeddedIndex and externalPath to identify a file.
"""
def __init__(self, partition, bucket, level, file_name,
extra_files, embedded_index, external_path):
self.partition = partition
self.bucket = bucket
self.level = level
self.file_name = file_name
self.extra_files = extra_files
self.embedded_index = embedded_index
self.external_path = external_path
self._hash = None
def __eq__(self, other):
if self is other:
return True
if other is None or not isinstance(other, FileEntry.Identifier):
return False
return (self.bucket == other.bucket
and self.level == other.level
and self.partition == other.partition
and self.file_name == other.file_name
and self.extra_files == other.extra_files
and self.embedded_index == other.embedded_index
and self.external_path == other.external_path)
def __hash__(self):
if self._hash is None:
self._hash = hash((
self.partition,
self.bucket,
self.level,
self.file_name,
self.extra_files,
self.embedded_index,
self.external_path,
))
return self._hash
def identifier(self):
"""Build a unique Identifier for this file entry.
Returns:
An Identifier instance.
"""
extra_files = (tuple(self.file.extra_files)
if self.file.extra_files else ())
return FileEntry.Identifier(
partition=self.partition,
bucket=self.bucket,
level=self.file.level,
file_name=self.file.file_name,
extra_files=extra_files,
embedded_index=self.file.embedded_index,
external_path=self.file.external_path,
)
@staticmethod
def merge_entries(entries):
"""Merge file entries: ADD and DELETE of the same file cancel each other.
- ADD: if identifier already in map, raise error; otherwise add to map.
- DELETE: if identifier already in map, remove both (cancel);
otherwise add to map.
Args:
entries: Iterable of FileEntry.
Returns:
List of merged FileEntry values, preserving insertion order.
Raises:
RuntimeError: If trying to add a file that is already in the map.
"""
entry_map = {}
for entry in entries:
entry_identifier = entry.identifier()
if entry.kind == 0: # ADD
if entry_identifier in entry_map:
raise RuntimeError(
"Trying to add file {} which is already added.".format(
entry.file.file_name))
entry_map[entry_identifier] = entry
elif entry.kind == 1: # DELETE
if entry_identifier in entry_map:
del entry_map[entry_identifier]
else:
entry_map[entry_identifier] = entry
else:
raise RuntimeError(
"Unknown entry kind: {}".format(entry.kind))
return list(entry_map.values())