Machine Coding Problem

Image Gallery

macoAllmediametadatafolder-structure
Commonly Asked By:PinterestSnapInstagram

Requirements & System Scope

Functional Scope (In-Scope)

  • Hierarchical Parent-Child Folder Tree: Represents multi-level directories uniformly using child and parent relationship matrices.
  • O(1) Tag-Based Index Intersect: O(1) query index enabling efficient multi-tag "AND" search querying without full repository scans.
  • Rich EXIF Media Metadata Store: Safely parses and indexes file size, layout constraints, spatial coordinates, and hardware camera specifications.
  • HMAC Cryptographic Shared Links: Encodes token payload parameters with expirations directly, validated statelessly via signature checking.

Explicit Boundaries (Out-of-Scope)

  • Concrete Blob Transcoding Engines: Omits real-time video/image compression algorithms, dynamic resolution resizing, and chunk loaders.
  • Network-level CDN and S3 integrations: Simulates local file path identifiers instead of implementing actual S3 API adapters or cloud bucket uploaders.

Class Diagram & Entity Relationships

Composite folder organization hierarchy and inverted tag lookup structures:

Loading...
  • Folder Entity: Retains list links referencing subfolders and raw image nodes (Composite Pattern base).
  • ExifMetadata Entity: Implements value-object properties detailing file metrics.

Design Patterns & SOLID Principles

  • Composite Pattern: Standardizes folder nesting by representing files and nested subdirectories uniformly under consistent identifier trees.
  • Inverted Index Design: Avoids linear scanning by structuring tag searches using an O(1) lookup set mapping tags directly to matched image IDs.
  • Single Responsibility Principle (SRP): Isolates image entities from EXIF extractors and token signing services.

Core Execution Workflows

Intersection Searching & Stateless Shared Access

  1. Multi-Tag "AND" Set Intersect Search:
    1. Receive list of search tags (e.g. {Nature, Hills}).
    2. Fetch matched set lists for each individual tag from the index map. If any tag has zero entries, immediately exit with empty results.
    3. Intersect the sets sequentially, dropping non-matching image IDs.
    4. Hydrate match identifiers into Image records, sorted chronologically.
  2. Stateless HMAC Link Generation:
    1. Concatenate target albumId and absolute expiry timestamp to form a payload key string.
    2. Generate an HMAC-SHA256 signature hash of the payload using the private server-side security secret.
    3. Append the base payload parameters and the signature signature directly as request URL parameters.

Concurrency & Thread Safety Strategy

Securing write updates during rapid user media uploads and index updates:

  • Concurrent Hierarchical Collections & Locks: Thread-safe directories and collections with ReentrantReadWriteLock in Java and RLock in Python protect the folder tree and metadata registry.
  • Isolated Index Updates: Tag updates utilize safe mapping features to avoid race conditions when indexing identical tags concurrently.

Complete Clean Code Blueprint

Production reference implementations demonstrating nested folders, O(1) multi-tag queries, and stateless HMAC links in Java and Python:

// โ”€โ”€โ”€ JAVA BLUEPRINT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

class ExifMetadata {
    private final long fileSizeBytes;
    private final int widthPixels;
    private final int heightPixels;
    private final String cameraModel;
    private final double gpsLatitude;
    private final double gpsLongitude;

    public ExifMetadata(long fileSizeBytes, int widthPixels, int heightPixels, String cameraModel, double gpsLatitude, double gpsLongitude) {
        this.fileSizeBytes = fileSizeBytes;
        this.widthPixels = widthPixels;
        this.heightPixels = heightPixels;
        this.cameraModel = cameraModel;
        this.gpsLatitude = gpsLatitude;
        this.gpsLongitude = gpsLongitude;
    }

    public long getFileSizeBytes() { return fileSizeBytes; }
    public int getWidthPixels() { return widthPixels; }
    public int getHeightPixels() { return heightPixels; }
    public String getCameraModel() { return cameraModel; }
    public double getGpsLatitude() { return gpsLatitude; }
    public double getGpsLongitude() { return gpsLongitude; }
}

class Image {
    private final String id;
    private final String filename;
    private final String filePath;
    private final long uploadTimeMs;
    private final Set<String> tags;
    private final ExifMetadata exifMetadata;
    private final String folderId;

    public Image(String id, String filename, String filePath, long uploadTimeMs, Set<String> tags, ExifMetadata exifMetadata, String folderId) {
        this.id = id;
        this.filename = filename;
        this.filePath = filePath;
        this.uploadTimeMs = uploadTimeMs;
        this.tags = new HashSet<>(tags);
        this.exifMetadata = exifMetadata;
        this.folderId = folderId;
    }

    public String getId() { return id; }
    public String getFilename() { return filename; }
    public String getFilePath() { return filePath; }
    public long getUploadTimeMs() { return uploadTimeMs; }
    public Set<String> getTags() { return Collections.unmodifiableSet(tags); }
    public ExifMetadata getExifMetadata() { return exifMetadata; }
    public String getFolderId() { return folderId; }
}

class Folder {
    private final String id;
    private final String name;
    private final String parentId; // null if Root
    private final List<String> subfolderIds;
    private final List<String> imageIds;

    public Folder(String id, String name, String parentId) {
        this.id = id;
        this.name = name;
        this.parentId = parentId;
        this.subfolderIds = new CopyOnWriteArrayList<>();
        this.imageIds = new CopyOnWriteArrayList<>();
    }

    public String getId() { return id; }
    public String getName() { return name; }
    public String getParentId() { return parentId; }
    public List<String> getSubfolderIds() { return subfolderIds; }
    public List<String> getImageIds() { return imageIds; }

    public void addSubfolder(String folderId) {
        subfolderIds.add(folderId);
    }

    public void addImage(String imageId) {
        imageIds.add(imageId);
    }

    public void removeImage(String imageId) {
        imageIds.remove(imageId);
    }
}

class GalleryService {
    private final Map<String, Folder> folders = new HashMap<>();
    private final Map<String, Image> images = new HashMap<>();
    private final Map<String, Set<String>> tagIndex = new HashMap<>();
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final String secretKey = "SuperSecretHMACGalleryKey";
    private static final String ROOT_FOLDER_ID = "ROOT";

    public GalleryService() {
        folders.put(ROOT_FOLDER_ID, new Folder(ROOT_FOLDER_ID, "Root", null));
    }

    public Folder createFolder(String name, String parentId) {
        String actualParent = (parentId == null) ? ROOT_FOLDER_ID : parentId;
        
        rwLock.writeLock().lock();
        try {
            Folder parent = folders.get(actualParent);
            if (parent == null) {
                throw new IllegalArgumentException("Parent folder does not exist.");
            }

            String folderId = UUID.randomUUID().toString();
            Folder newFolder = new Folder(folderId, name, actualParent);
            folders.put(folderId, newFolder);
            parent.addSubfolder(folderId);

            return newFolder;
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public Image uploadImage(String filename, String filePath, Set<String> tags, ExifMetadata exif, String folderId) {
        String actualFolder = (folderId == null) ? ROOT_FOLDER_ID : folderId;
        
        rwLock.writeLock().lock();
        try {
            Folder target = folders.get(actualFolder);
            if (target == null) {
                throw new IllegalArgumentException("Target folder does not exist.");
            }

            String imageId = UUID.randomUUID().toString();
            Image newImage = new Image(imageId, filename, filePath, System.currentTimeMillis(), tags, exif, actualFolder);
            images.put(imageId, newImage);
            target.addImage(imageId);

            // Populate Tag Index O(1)
            for (String tag : tags) {
                tagIndex.computeIfAbsent(tag.toLowerCase(), k -> new HashSet<>()).add(imageId);
            }

            return newImage;
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public List<Image> searchImagesByTags(Set<String> queryTags) {
        if (queryTags == null || queryTags.isEmpty()) {
            return Collections.emptyList();
        }

        rwLock.readLock().lock();
        try {
            Set<String> intersectedIds = null;

            for (String tag : queryTags) {
                Set<String> matches = tagIndex.get(tag.toLowerCase());
                if (matches == null || matches.isEmpty()) {
                    return Collections.emptyList(); // AND constraint fails
                }
                if (intersectedIds == null) {
                    intersectedIds = new HashSet<>(matches);
                } else {
                    intersectedIds.retainAll(matches);
                }
            }

            if (intersectedIds == null || intersectedIds.isEmpty()) {
                return Collections.emptyList();
            }

            return intersectedIds.stream()
                .map(images::get)
                .sorted(Comparator.comparingLong(Image::getUploadTimeMs).reversed())
                .collect(Collectors.toList());
        } finally {
            rwLock.readLock().unlock();
        }
    }

    public String generateShareLink(String albumId, long ttlSeconds) {
        rwLock.readLock().lock();
        try {
            if (!folders.containsKey(albumId)) {
                throw new IllegalArgumentException("Target album/folder does not exist.");
            }
        } finally {
            rwLock.readLock().unlock();
        }
        
        long expiryTimeMs = System.currentTimeMillis() + (ttlSeconds * 1000);
        String payload = albumId + ":" + expiryTimeMs;
        String signature = generateHMAC(payload);

        return "https://gallery.app/shared?albumId=" + albumId + "&expires=" + expiryTimeMs + "&sig=" + signature;
    }

    public boolean validateShareLink(String albumId, long expiryTimeMs, String signature) {
        if (System.currentTimeMillis() > expiryTimeMs) {
            return false; // Link expired
        }
        String payload = albumId + ":" + expiryTimeMs;
        String expectedSignature = generateHMAC(payload);
        return constantTimeEquals(expectedSignature, signature);
    }

    public Folder getFolderContents(String folderId) {
        rwLock.readLock().lock();
        try {
            return folders.get(folderId);
        } finally {
            rwLock.readLock().unlock();
        }
    }

    public Image getImage(String imageId) {
        rwLock.readLock().lock();
        try {
            return images.get(imageId);
        } finally {
            rwLock.readLock().unlock();
        }
    }

    private String generateHMAC(String data) {
        try {
            Mac sha256HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            sha256HMAC.init(secretKeySpec);
            byte[] hash = sha256HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8));
            
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new RuntimeException("HMAC computation failed", e);
        }
    }

    private boolean constantTimeEquals(String a, String b) {
        if (a == null || b == null) return false;
        if (a.length() != b.length()) return false;
        
        int result = 0;
        for (int i = 0; i < a.length(); i++) {
            result |= a.charAt(i) ^ b.charAt(i);
        }
        return result == 0;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println("=== JAVA IMAGE GALLERY DEMO ===");
        GalleryService service = new GalleryService();

        // 1. Create album folders
        Folder vacationAlbum = service.createFolder("Vacation 2026", null);
        Folder subFolderPhotos = service.createFolder("Scenery", vacationAlbum.getId());
        System.out.println("Created folder hierarchy structure.");

        // 2. Upload images with EXIF metadata
        ExifMetadata exif1 = new ExifMetadata(5420000L, 4032, 3024, "iPhone 15 Pro", 37.7749, -122.4194);
        Image img1 = service.uploadImage("sunset.png", "/vacation/scenery/sunset.png", 
            new HashSet<>(Arrays.asList("nature", "sunset", "scenery")), exif1, subFolderPhotos.getId());
        System.out.println("Uploaded Sunset Photo to scenery album: " + img1.getId());

        ExifMetadata exif2 = new ExifMetadata(2150000L, 2048, 1536, "Canon EOS", 34.0522, -118.2437);
        Image img2 = service.uploadImage("mountains.jpg", "/vacation/scenery/mountains.jpg", 
            new HashSet<>(Arrays.asList("nature", "mountain", "scenery")), exif2, subFolderPhotos.getId());
        System.out.println("Uploaded Mountains Photo to scenery album: " + img2.getId());

        // 3. Search photos via Multi-Tag AND intersection lookup
        Set<String> searchTags = new HashSet<>(Arrays.asList("nature", "scenery"));
        List<Image> searchResults = service.searchImagesByTags(searchTags);
        System.out.println("Tag search for [nature, scenery] found " + searchResults.size() + " matches:");
        for (Image img : searchResults) {
            System.out.println(" - " + img.getFilename() + " (Camera: " + img.getExifMetadata().getCameraModel() + ")");
        }

        // 4. Shared stateful link generation with secure HMAC validation
        String shareLink = service.generateShareLink(vacationAlbum.getId(), 2); // 2-second TTL expiration
        System.out.println("Generated secure stateless share link:\n " + shareLink);

        // Parse query params to validate link
        String albumIdParam = vacationAlbum.getId();
        long expiresParam = System.currentTimeMillis() + 2000;
        String sigParam = shareLink.substring(shareLink.lastIndexOf("sig=") + 4);

        boolean isValidImmediate = service.validateShareLink(albumIdParam, expiresParam, sigParam);
        System.out.println("Validation check immediately: " + (isValidImmediate ? "Valid Access" : "Access Denied"));

        System.out.println("Sleeping 2.5 seconds to force HMAC token expiration...");
        Thread.sleep(2500);

        boolean isValidPostSleep = service.validateShareLink(albumIdParam, expiresParam, sigParam);
        System.out.println("Validation check after expiration: " + (isValidPostSleep ? "Valid Access" : "Access Denied"));
        System.out.println("=== END OF JAVA DEMO ===");
    }
}

๐Ÿ’ฌReview

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...