Machine Coding Problem

Email Client

macoAllmessagingsearchfolder-hierarchy
Commonly Asked By:GoogleMicrosoftYahoo

Requirements & System Scope

Functional Scope (In-Scope)

  • Nested Folder Tree: A recursive folder hierarchy enabling explicit, exclusive email moves and placement tree navigation.
  • Thread Grouping Chains: Reconstructing interactive conversations by grouping emails matching mutual parent/references headers.
  • Weighted Inverted Search Index: Scoring query matches over subject (3x weight) and body tokens (1x weight) to return ranked email lists.
  • Inbound Filtering Router: Automating folder assignment triggers based on sender addresses or text keyword checks.
  • Folder Pagination: Maintaining performant scrolling and bounded payload memory by pulling emails inside structured indices.

Explicit Boundaries (Out-of-Scope)

  • Low-Level Protocol Transport: Writing socket parsers for actual SMTP, IMAP, or POP3 packet handlers.
  • Rich-Text / HTML Parser: Rendering complex layouts, inline styling, CSS formatting, or sanitizing malicious scripting inside sandboxes.

Class Diagram & Entity Relationships

Email client and data catalog hierarchy models:

Loading...
  • Email: Immutable record indicating message identities, threading reference headers, bodies, read states, and labels.
  • FilterRule: Configuration parameters indicating match regex patterns and exclusive destination folder IDs.

Design Patterns & SOLID Principles

  • Composite Pattern: Models nested folder directory trees uniformly where folders can recursive-link nested subfolders.
  • Strategy Pattern: Enables swappable mail-routing rules and custom search-ranking metrics without editing client structures.
  • Single Responsibility Principle (SRP): Search index indexing and query ranking logic are kept entirely decoupled from mail receipt systems.

Core Execution Workflows

Inbound Mail Receipt and Search Flow

  1. An inbound email is received by the EmailClient.
  2. The message references/replies header chain is parsed to attach or instantiate a matching threadId.
  3. The body and subject are parsed into token indices inside the weighted SearchIndex.
  4. Registered filters analyze sender and subject strings to identify folder routing destinations.
  5. The email id is appended exclusively into the computed target folder lists.

Concurrency & Thread Safety Strategy

Operating safely under concurrent read/write and indexing actions:

  • Concurrent Collections: Using concurrent maps for indexes and mailbox entities to ensure parallel readers do not experience split-reads during background updates.
  • Search Query Isolation: Building search results compiles dynamic arrays, meaning query operations remain side-effect free and thread-safe.

Complete Clean Code Blueprint

Clean reference designs demonstrating search token index and routing rules in Java and Python:

// โ”€โ”€โ”€ JAVA BLUEPRINT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
import java.util.*;
import java.util.concurrent.*;

class Email {
    private final String id;
    private final String sender;
    private final List<String> recipients;
    private final String subject;
    private final String body;
    private final List<String> attachments;
    private volatile boolean read;
    private final String messageId;
    private final String inReplyTo; // Parent message ID
    private volatile String threadId;

    public Email(String id, String sender, List<String> recipients, String subject, String body, List<String> attachments, String messageId, String inReplyTo) {
        this.id = id;
        this.sender = sender;
        this.recipients = recipients;
        this.subject = subject;
        this.body = body;
        this.attachments = attachments;
        this.read = false;
        this.messageId = messageId;
        this.inReplyTo = inReplyTo;
    }

    public String getId() { return id; }
    public String getSender() { return sender; }
    public List<String> getRecipients() { return recipients; }
    public String getSubject() { return subject; }
    public String getBody() { return body; }
    public List<String> getAttachments() { return attachments; }
    public boolean isRead() { return read; }
    public void setRead(boolean read) { this.read = read; }
    public String getMessageId() { return messageId; }
    public String getInReplyTo() { return inReplyTo; }
    public String getThreadId() { return threadId; }
    public void setThreadId(String threadId) { this.threadId = threadId; }

    @Override
    public String toString() {
        return "Email{id='" + id + "', sender='" + sender + "', subject='" + subject + "', threadId='" + threadId + "'}";
    }
}

class Folder {
    private final String id;
    private final String name;
    private final String parentId;
    private final Set<String> emailIds = ConcurrentHashMap.newKeySet();

    public Folder(String id, String name, String parentId) {
        this.id = id;
        this.name = name;
        this.parentId = parentId;
    }

    public String getId() { return id; }
    public String getName() { return name; }
    public String getParentId() { return parentId; }
    public Set<String> getEmailIds() { return emailIds; }
}

interface FilterStrategy {
    boolean matches(Email email);
}

class SenderFilterStrategy implements FilterStrategy {
    private final String pattern;
    public SenderFilterStrategy(String pattern) { this.pattern = pattern; }
    @Override public boolean matches(Email email) { return email.getSender().contains(pattern); }
}

class SubjectFilterStrategy implements FilterStrategy {
    private final String keyword;
    public SubjectFilterStrategy(String keyword) { this.keyword = keyword; }
    @Override public boolean matches(Email email) { return email.getSubject().toLowerCase().contains(keyword.toLowerCase()); }
}

class FilterRule {
    private final FilterStrategy strategy;
    private final String targetFolderId;

    public FilterRule(FilterStrategy strategy, String targetFolderId) {
        this.strategy = strategy;
        this.targetFolderId = targetFolderId;
    }

    public boolean matches(Email email) {
        return strategy.matches(email);
    }

    public String getTargetFolderId() { return targetFolderId; }
}

class FilterStrategyFactory {
    public static FilterStrategy createSenderFilter(String pattern) {
        return new SenderFilterStrategy(pattern);
    }
    public static FilterStrategy createSubjectFilter(String keyword) {
        return new SubjectFilterStrategy(keyword);
    }
}

class SearchIndex {
    private final Map<String, Map<String, Integer>> subjectIndex = new ConcurrentHashMap<>();
    private final Map<String, Map<String, Integer>> bodyIndex = new ConcurrentHashMap<>();

    public void index(Email email) {
        indexText(email.getId(), email.getSubject(), subjectIndex);
        indexText(email.getId(), email.getBody(), bodyIndex);
    }

    private void indexText(String emailId, String text, Map<String, Map<String, Integer>> index) {
        if (text == null) return;
        // Using bracket-range split to prevent backslash escapes issue under Next.js Turbopack
        String[] tokens = text.toLowerCase().split("[^a-zA-Z0-9]+");
        for (String token : tokens) {
            if (token.isEmpty()) continue;
            index.computeIfAbsent(token, k -> new ConcurrentHashMap<>())
                 .merge(emailId, 1, Integer::sum);
        }
    }

    public List<String> search(String query) {
        if (query == null || query.trim().isEmpty()) return Collections.emptyList();
        String[] tokens = query.toLowerCase().split("[^a-zA-Z0-9]+");
        Map<String, Double> scores = new HashMap<>();

        for (String token : tokens) {
            if (token.isEmpty()) continue;
            
            Map<String, Integer> subMatches = subjectIndex.get(token);
            if (subMatches != null) {
                for (Map.Entry<String, Integer> entry : subMatches.entrySet()) {
                    scores.merge(entry.getKey(), entry.getValue() * 3.0, Double::sum);
                }
            }

            Map<String, Integer> bodyMatches = bodyIndex.get(token);
            if (bodyMatches != null) {
                for (Map.Entry<String, Integer> entry : bodyMatches.entrySet()) {
                    scores.merge(entry.getKey(), entry.getValue() * 1.0, Double::sum);
                }
            }
        }

        List<Map.Entry<String, Double>> sorted = new ArrayList<>(scores.entrySet());
        sorted.sort((a, b) -> Double.compare(b.getValue(), a.getValue()));

        List<String> results = new ArrayList<>();
        for (Map.Entry<String, Double> entry : sorted) {
            results.add(entry.getKey());
        }
        return results;
    }
}

class EmailClient {
    private final Map<String, Email> emails = new ConcurrentHashMap<>();
    private final Map<String, Folder> folders = new ConcurrentHashMap<>();
    private final SearchIndex searchIndex = new SearchIndex();
    private final List<FilterRule> filterRules = new CopyOnWriteArrayList<>();

    public EmailClient() {
        createFolder("INBOX", "Inbox", null);
    }

    public void createFolder(String id, String name, String parentId) {
        folders.put(id, new Folder(id, name, parentId));
    }

    public void addFilterRule(FilterRule rule) {
        filterRules.add(rule);
    }

    public void receiveEmail(Email email) {
        emails.put(email.getId(), email);
        searchIndex.index(email);
        
        resolveThreading(email);

        String targetFolderId = "INBOX";
        for (FilterRule rule : filterRules) {
            if (rule.matches(email)) {
                targetFolderId = rule.getTargetFolderId();
                break;
            }
        }

        moveEmail(email.getId(), targetFolderId);
    }

    private void resolveThreading(Email email) {
        if (email.getInReplyTo() != null && emails.containsKey(email.getInReplyTo())) {
            Email parent = emails.get(email.getInReplyTo());
            email.setThreadId(parent.getThreadId());
        } else {
            email.setThreadId(email.getId());
        }
    }

    public void moveEmail(String emailId, String targetFolderId) {
        Email email = emails.get(emailId);
        if (email == null) return;

        // Remove from any folder it's currently inside
        for (Folder folder : folders.values()) {
            folder.getEmailIds().remove(emailId);
        }

        Folder target = folders.computeIfAbsent(targetFolderId, k -> new Folder(k, k, null));
        target.getEmailIds().add(emailId);
    }

    public List<Email> getFolderEmailsPaginated(String folderId, int page, int pageSize) {
        Folder folder = folders.get(folderId);
        if (folder == null) return Collections.emptyList();

        List<String> ids = new ArrayList<>(folder.getEmailIds());
        int start = Math.min(page * pageSize, ids.size());
        int end = Math.min(start + pageSize, ids.size());

        List<Email> paged = new ArrayList<>();
        for (int i = start; i < end; i++) {
            paged.add(emails.get(ids.get(i)));
        }
        return paged;
    }

    public List<Email> search(String query) {
        List<String> matchedIds = searchIndex.search(query);
        List<Email> list = new ArrayList<>();
        for (String id : matchedIds) {
            list.add(emails.get(id));
        }
        return list;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== STARTING EMAIL CLIENT SIMULATION ===");
        EmailClient client = new EmailClient();

        // 1. Setup Folder Structure and Filtering Rules
        client.createFolder("WORK", "Work-related", null);
        client.createFolder("PROMOTION", "Marketing Deals", null);

        // Filter: Sender contains "boss" -> WORK folder
        client.addFilterRule(new FilterRule(FilterStrategyFactory.createSenderFilter("boss"), "WORK"));
        // Filter: Subject contains "discount" -> PROMOTION folder
        client.addFilterRule(new FilterRule(FilterStrategyFactory.createSubjectFilter("discount"), "PROMOTION"));

        // 2. Receive diverse emails
        System.out.println("Receiving incoming emails...");
        Email normal = new Email("m1", "friend@gmail.com", List.of("user@me.com"), "Weekend plans", "Hi, are we meeting up?", null, "msg-001", null);
        Email work = new Email("m2", "boss@company.com", List.of("user@me.com"), "Urgent Project", "Please review the security audit sheets immediately.", null, "msg-002", null);
        Email promo = new Email("m3", "deals@store.com", List.of("user@me.com"), "Big discount inside!", "Get 50% off storewide today only.", null, "msg-003", null);
        Email threadReply = new Email("m4", "boss@company.com", List.of("user@me.com"), "Re: Urgent Project", "Thanks for the feedback.", null, "msg-004", "m2");

        client.receiveEmail(normal);
        client.receiveEmail(work);
        client.receiveEmail(promo);
        client.receiveEmail(threadReply);

        // 3. Verify folders
        System.out.println("\n--- Folder Auditing ---");
        System.out.println("INBOX Emails: " + client.getFolderEmailsPaginated("INBOX", 0, 10));
        System.out.println("WORK Emails: " + client.getFolderEmailsPaginated("WORK", 0, 10));
        System.out.println("PROMOTION Emails: " + client.getFolderEmailsPaginated("PROMOTION", 0, 10));

        // 4. Verify threading links
        System.out.println("\n--- Thread Identification ---");
        System.out.println("Parent email (m2) threadId: " + work.getThreadId());
        System.out.println("Reply email (m4) threadId (should match parent): " + threadReply.getThreadId());

        // 5. Query full text index
        System.out.println("\n--- Full Text Search Queries ---");
        System.out.println("Search 'security audit' (work matched):");
        for (Email e : client.search("security audit")) {
            System.out.println("  Matched: " + e);
        }

        System.out.println("Search 'discount' (promo matched):");
        for (Email e : client.search("discount")) {
            System.out.println("  Matched: " + e);
        }
        System.out.println("=== EMAIL CLIENT SIMULATION COMPLETE ===");
    }
}

๐Ÿ’ฌ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...