Machine Coding Problem

Email Client

macoAllmessagingsearchfolder-hierarchy
Commonly Asked By:GoogleMicrosoftYahoo

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.

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 ===");
    }
}