← Back

In-Memory File Lookup System

Medium
Question

In-Memory File Lookup System (Unix find-like)

Build an in-memory file lookup tool similar to the Unix find command.

The system should support two initial search criteria and be easily extensible so new search criteria can be added later.

Use the Strategy Design Pattern to implement the search criteria.


Functional Requirements

Supported Search Criteria

  • Search by Minimum File Size

    Return all files strictly larger than a given size (in MB) under a specified directory recursively.

  • Search by File Extension

    Return all files with a given file extension under a specified directory recursively. Example: .xml, .jpg


API Design

Use Java-style naming conventions.

void putFile(String path, int sizeMb)

Behavior

  • Adds a new file entry.
  • If the file already exists → overwrite the size.
  • path will always be a valid absolute file path.
  • sizeMb >= 0

List<String> search(int searchCriteriaId, String dirPath, String args)

Behavior

Find files matching a Search Criteria.

Requirements

  • Search must include all nested subdirectories recursively
  • Return file paths sorted in ascending lexicographical order
  • No duplicate paths

Search Criteria Arguments

CriteriasearchCriteriaIdargs formatExample
Minimum File Size1"minSizeMb""5"
File Extension2".ext"".xml"

Examples:

search(1, "/data", "8")
search(2, "/work", ".xml")

Design Requirement

Use the Strategy Design Pattern for implementing search criteria.

This ensures the system can easily support new search criteria in the future, such as:

  • Filename substring match
  • Created date
  • Modified date
  • File size range
  • Regex match

Example 1

FileSearch s = new FileSearch();

s.putFile("/data/pics/photoA.jpg", 4);
s.putFile("/data/pics/movie.mp4", 12);
s.putFile("/work/docs/readme.md", 1);
s.putFile("/work/docs/report.xml", 7);

Update file size:

s.putFile("/data/pics/photoA.jpg", 9);

Search Criteria 1

Files > 8 MB inside /data

s.search(1, "/data", "8");

Result:

[
"/data/pics/photoA.jpg",
"/data/pics/movie.mp4"
]

Search Criteria 2

Files with .xml inside /work

s.search(2, "/work", ".xml");

Result:

[
"/work/docs/report.xml"
]

Example 2

FileSearch s = new FileSearch();

s.putFile("/media/images/aa.jpg", 6);
s.putFile("/media/images/ab.jpg", 7);
s.putFile("/media/images/ac.xml", 2);

s.putFile("/office/reports/r1.xml", 9);
s.putFile("/office/reports/r2.xml", 4);
s.putFile("/office/notes.txt", 3);

Search Criteria 1

Files > 5 MB under /media/images

s.search(1, "/media/images", "5");

Result:

[
"/media/images/aa.jpg",
"/media/images/ab.jpg"
]

Search Criteria 2

Files with .xml under /office (recursive)

s.search(2, "/office", ".xml");

Result:

[
"/office/reports/r1.xml",
"/office/reports/r2.xml"
]

Constraints

  • At most 1000 total calls to putFile and search
  • Maximum 2000 files stored
  • All paths are absolute (start with /)
  • Directory separator is /
  • sizeMb range: 0 → 10000
  • search must traverse all nested directories recursively
  • Return results in strict lexicographical order
Solution

Solution: In-Memory File Lookup System

This is a classic LLD + Strategy Pattern question. The interviewer checks:

  • Open/Closed Principle
  • Extensible search logic
  • Clean separation of responsibilities

I'll show a production-quality C++ design.


Design Idea

We separate the system into three responsibilities:

1. File storage

Stores files in memory.

path -> size

2. Search Strategy

Each search criteria implements a strategy interface.

SearchStrategy
   ↑
SizeSearchStrategy
ExtensionSearchStrategy

3. Search Engine

Handles:

  • directory filtering
  • applying strategy
  • sorting results

Why Strategy Pattern?

Without strategy:

if(criteria == 1) ...
else if(criteria == 2) ...

Every new feature requires modifying the main logic ❌

With strategy:

strategy->match(file)

Add new search criteria without touching existing code

Example future extension:

NameContainsStrategy
CreatedAfterStrategy
OwnerStrategy

Data Model

struct File {
    string path;
    int sizeMb;
};

Storage:

unordered_map<string, int> files

Key = path Value = size


Strategy Interface

class SearchStrategy {
public:
    virtual bool match(const string& path, int sizeMb, const string& args) = 0;
    virtual ~SearchStrategy() {}
};

Strategy 1 — Size Search

Criteria: size > minSize

class SizeSearchStrategy : public SearchStrategy {
public:
    bool match(const string& path, int sizeMb, const string& args) override {
        int minSize = stoi(args);
        return sizeMb > minSize;
    }
};

Strategy 2 — Extension Search

Criteria: file ends with extension

class ExtensionSearchStrategy : public SearchStrategy {
public:
    bool match(const string& path, int sizeMb, const string& args) override {
        string ext = args;

        if (path.size() < ext.size()) return false;

        return path.substr(path.size() - ext.size()) == ext;
    }
};

Main FileSearch System

Key responsibilities:

  • store files
  • map searchCriteriaId → strategy
  • filter directory
  • apply strategy

Full Optimal C++ Implementation

#include <bits/stdc++.h>
using namespace std;

struct File {
    string path;
    int sizeMb;
};

class SearchStrategy {
public:
    virtual bool match(const string& path, int sizeMb, const string& args) = 0;
    virtual ~SearchStrategy() {}
};

class SizeSearchStrategy : public SearchStrategy {
public:
    bool match(const string& path, int sizeMb, const string& args) override {
        int minSize = stoi(args);
        return sizeMb > minSize;
    }
};

class ExtensionSearchStrategy : public SearchStrategy {
public:
    bool match(const string& path, int sizeMb, const string& args) override {
        if (path.size() < args.size()) return false;
        return path.substr(path.size() - args.size()) == args;
    }
};

class FileStorage {
private:
    unordered_map<string, int> files;

public:
    void putFile(const string& path, int sizeMb) {
        files[path] = sizeMb;
    }

    const unordered_map<string, int>& getAllFiles() const {
        return files;
    }
};

class FileSearch {
private:
    FileStorage storage;
    unordered_map<int, SearchStrategy*> strategies;

    bool isUnderDirectory(const string& path, const string& dir) {
        if (path.size() < dir.size()) return false;
        if (path.substr(0, dir.size()) != dir) return false;

        if (path.size() == dir.size()) return true;
        return path[dir.size()] == '/';
    }

public:
    FileSearch() {
        strategies[1] = new SizeSearchStrategy();
        strategies[2] = new ExtensionSearchStrategy();
    }

    void putFile(string path, int sizeMb) {
        storage.putFile(path, sizeMb);
    }

    vector<string> search(int searchCriteriaId, string dirPath, string args) {

        vector<string> result;

        if (!strategies.count(searchCriteriaId)) return result;

        SearchStrategy* strategy = strategies[searchCriteriaId];

        auto& files = storage.getAllFiles();

        for (auto& [path, size] : files) {

            if (!isUnderDirectory(path, dirPath)) continue;

            if (strategy->match(path, size, args))
                result.push_back(path);
        }

        sort(result.begin(), result.end());

        return result;
    }

    ~FileSearch() {
        for (auto& [id, strategy] : strategies)
            delete strategy;
    }
};

int main() {
    FileSearch fs;

    // Adding files
    fs.putFile("/office/reports/a.xml", 5);
    fs.putFile("/office/reports/b.xml", 15);
    fs.putFile("/office/reports/c.txt", 20);
    fs.putFile("/office/data/d.xml", 25);
    fs.putFile("/home/user/e.xml", 30);

    cout << "---- Search: Size > 10 in /office ----\n";

    vector<string> res1 = fs.search(1, "/office", "10");

    for (auto& file : res1) {
        cout << file << "\n";
    }

    cout << "\n---- Search: Extension .xml in /office/reports ----\n";

    vector<string> res2 = fs.search(2, "/office/reports", ".xml");

    for (auto& file : res2) {
        cout << file << "\n";
    }

    return 0;
}

Complexity

Let:

N = number of files (≤2000)

putFile

O(1)

search

O(N log N)
  • scan all files
  • sort results

Perfect for constraints.


Why This Design Is Good (Interview Answer)

Open/Closed Principle

The system is open for extension but closed for modification.

To add new criteria:

class NameContainsStrategy : public SearchStrategy

Then just register:

strategies[3] = new NameContainsStrategy();

No other code changes required.


Possible Follow-ups (Interviewers LOVE these)

Add search by filename substring

search(3, "/data", "report")

Add multiple criteria

size > 10 AND extension = ".xml"

Optimize search

Use directory index (Trie) instead of scanning all files.