Search code examples
node.jstypescriptmongoosenestedsubdocument

Mongoose - Nested SubDocuments linked to same type


I have the structure as following:

--> means is included in (as property):
Item --> ItemStack <--> Inventory

Item: name: string, weight: number, ...
ItemStack: item: Item, amount: number, inventory: Inventory | null
Inventory: items: ItemStack[], capacity: number

An Item is the general definition of some Item. The ItemStack has a reference to the item and an amount of how many of these items are there. However, an Item can be a backpack, and a backpack can have an Inventory. So, the ItemStack also has an Inventory as a subdocument. And the Inventory then stores an array of ItemStacks. That's the problem, since ItemStack and Inventory should not have their own collections, as they're not in many-to-many-relations, it would be best to have them stored as subdocuments. Here comes the problem, as this may be seen as a circular reference, which can never happen, though.

That's how it might look in action (when retrieved from the database):

Item {
  id: 1,
  name: "Apple",
  weight: 1
}
Item {
  id: 2,
  name: "Backpack",
  weight: 10
}

InventoryHolder {
  Inventory {
    capacity: 10,
    items: [
      Item {
        id: 1,
        name: "Apple"
      },
      Item {
        id: 2,
        name: "Backpack",
        inventory: Inventory { // the point of circular reference
          items: [] // as many more items in backpacks as needed
        }
      }
    ]
  }
}

However, as an Inventory can only be held or owned by one single Holder or Item, storing them as subdocuments would be best.

My problem now is on how to define the models for the structure to work just like this.

Thanks in advance!

EDIT: I've tried working with Typegoose, which lets me define classes for models. That's how I tried to set them up, according to having them as subdocuments:

class TSItem extends Typegoose {
    @prop()
    name?: string;
    @prop()
    weight?: number;
    @prop()
    canHaveInventory?: boolean;
}

class TSItemStack extends Typegoose {
    @prop()
    item?: TSItem;
    @prop()
    inventory?: TSInventory;
    @prop()
    amount?: number;
}

class TSInventory {
    items?: TSItemStack[];
    capacity?: number;

    constructor(){
        this.items = [];
        this.capacity = 10;
    }
}

When compiling it however, I obviously get this error:

ReferenceError: TSInventory is not defined

... as I'm trying to use it before it was defined. And that's the exact problem. The types themselves are in a circular relation, however when applying it with real data, this would never happen.


Solution

  • Managed to simply use "Object" as the type for the recursion/circular reference.

    Will require me to do some typecasting afterwards, but it works! Can't seem to solve the problem any other way.