How to query recursive MongoDB Document [File System of files and folders]

449 Views Asked by At

I am developing an application that uses MongoDB (typegoose) to store file system-like (recursive) documents composed of files and folders. However, I do not know how to query such a schema. For querying, I am provided the following.

  1. users _id
  2. the array of folder names in order ex. ['root', 'nestedFolder1', 'nestedFolder2', 'etc...']
  3. _id of the last folder chosen

The schema is as follows

import { Types } from "mongoose";
interface File {
    _id: Types.ObjectId;
    fileName: string;
    isDir: false;
    content: string;
    title: string;
    description: string;
}

interface FileSystem {
    _id: Types.ObjectId;
    folderName: string;
    isDir: true;
    files: File[] | [];
    folders: FileSystem[] | [];
}

interface Project {
    _id: Types.ObjectId;
    projectName: string;
    fileSystem: FileSystem;
}
interface User {
    _id: Types.ObjectId;
    projects: Project[];
}

Update: Here's a replit for reference https://replit.com/@alphacoma18/mongodb-recursive-schema#server.ts

1

There are 1 best solutions below

5
On

1- Most trivial query, get all FileSystem Documents:

const fileSystems = await FileSystem.find({
    // Empty Object means no condition thus give me all your data
});

2- Using the $eq operator to match documents with a certain value

const fileSystems = await FileSystem.find({

        isDir: true,
        folderName: { $eq: 'root' }
    });

3- Using the $in operator to match a specific field with an array of possible values.

const fileSystems = await FileSystem.find({
    isDir: true,
    folderName: { $in: ['root', 'nestedFolder1', 'nestedFolder2'] }
});

4- Using the $and operator to specify multiple conditions

const fileSystems = await FileSystem.find({
    $and: [
        { isDir: true },
        { folderName: { $in: ['root', 'nestedFolder1', 'nestedFolder2'] } }
    ]
});

5- Retrieve all documents that are directories and are not empty.

const fileSystems = await FileSystem.find({
    isDir: true,
    $or: [
        { files: { $exists: true, $ne: [] } },
        { folders: { $exists: true, $ne: [] } }
    ]
});

6- All directories that have a folder name that starts with the letter 'n'

const fileSystems = await FileSystem.find({
    isDir: true,
    folderName: { $regex: '^n' }
});

Now the tougher queries:

1- count the total number of documents

const count = await FileSystem.aggregate([
    { $count: 'total' }
]);

2- count the total number of documents that are directories

const count = await FileSystem.aggregate([
    { $match: { isDir: true } },
    { $count: 'total' }
]);

3- Get 'foldername' and 'filename; of all documents that are files.

const fileSystems = await FileSystem.aggregate([
    { $match: { isDir: false } },
    { $project: { folderName: 1, fileName: 1 } }
]);

4- Retrieve the total number of files (isDir: false) in each directory (isDir: true)

const fileCounts = await FileSystem.aggregate([
    { $match: { isDir: true } },
    {
        $project: {
            folderName: 1,
            fileCount: {
                $size: {
                    $filter: {
                        input: '$files',
                        as: 'file',
                        cond: { $eq: ['$$file.isDir', false] }
                    }
                }
            }
        }
    }
]);

5- Recursive structure querying: The FileSystem interface is recursive, because it has both an array of files and an array of folders, which are both of type File[] and FileSystem[].

To query this recursive schema, you can use the $lookup operator in an aggregate pipeline, to make a left join between FileSystem and itself, based on some criteria.

//Retrieve All Documents from FileSystem and their child documents
const fileSystems = await FileSystem.aggregate([
    {
        $lookup: {
            from: 'FileSystem',
            localField: '_id',
            foreignField: 'parentId',
            as: 'children'
        }
    }
]);

//Use the match operator in the pipeline to filter the results: 
const fileSystems = await FileSystem.aggregate([
    {
        $lookup: {
            from: 'FileSystem',
            localField: '_id',
            foreignField: 'parentId',
            as: 'children'
        }
    },
    { $match: { isDir: true } }
]);