· I make my own rules

Updating item under nested array of custom data type in firebase

I am developing a module for a trade fair ERP system that aims to create a virtual representation of a trade fair booth. The booth features various walls (left, right, back) with carpets displayed on them, as well as multiple stacks of carpets on the floor and sample stacks. The goal is to mirror the physical arrangement of carpets in a virtual setup accurately.

To achieve this, I have created the following data structures:

  1. rugStackItem Schema:

    • rugRef: A Firestore DocumentReference to a carpet document.

    • positionInStack: An integer indicating the carpet's position in its respective stack.

  2. standSetup Schema:

    • leftWallStack, rightWallStack, backWall: Lists that contain rugStackItem objects, representing carpets hanging on the respective walls.

    • floorStacks, samples: Lists of floorStack objects. Each floorStack contains a list of rugStackItem objects, representing the stacks of carpets on the floor.

  3. floorStack Schema:

    • stackPosition: A string indicating the stack's position (e.g., "front-left", "back-center").

    • stackItems: A list of rugStackItem objects.

    • positionFromLeft: An integer representing the stack's position from the left side of the floor.

The challenge arises when attempting to add or remove carpets from the floorStacks or samples. These actions involve updating a nested array within a floorStack object, which Firestore does not support directly. I am experiencing the unintended consequence of the entire array being overwritten when I try to update a single element within it.

Here is a snippet of the Dart function used for adding/removing carpets:

dartCopy code

// Dart function to add or remove carpets Future<List<RugStackItemStruct>?> tradeFairAddOrRemoveRug( // function parameters ) async { // function logic // Firestore update operations }

For walls (leftWallStack, rightWallStack, backWall), the add/remove functionality works flawlessly. However, for floorStacks and samples, which are arrays of floorStack objects (which in turn contain arrays of rugStackItem), the same operation fails due to Firestore's array handling limitations.

Here are my questions:

  1. How can I perform add/remove operations within nested arrays in Firestore while maintaining the integrity of the other elements?

  2. Are there alternative data structuring strategies that could simplify these operations within Firestore's constraints?

  3. Has anyone faced and overcome a similar challenge with Firestore arrays, and if so, how?

Any insights, code snippets, or strategy suggestions would be incredibly helpful as I navigate this complex issue.

Thank you in advance for your time and assistance.

My schema screenshots and complete code is below


Future<List<RugStackItemStruct>?> tradeFairAddOrRemoveRug(

  String stackName,
  DocumentReference? rugRef,
  TradeFairsRecord? tradeFairDoc,
  bool isDelete,
) async {
  if (rugRef == null || tradeFairDoc == null) {
    print('rugRef or tradeFairDoc is null');
    return null;
  }

  print('Operation starts...');

  List<RugStackItemStruct>? targetStack;
  String firestoreFieldName = '';
  int stackIndex = -1;
  bool isWallStack = false;

  // Print initial floorStack array
  print('Initial floorStack: ${tradeFairDoc.standSetup.floorStacks}');

  // Check the walls first
  switch (stackName) {
    case 'Left Wall':
      firestoreFieldName = 'leftWallStack';
      targetStack = tradeFairDoc.standSetup.leftWallStack;
      isWallStack = true;
      break;
    case 'Right Wall':
      firestoreFieldName = 'rightWallStack';
      targetStack = tradeFairDoc.standSetup.rightWallStack;
      isWallStack = true;
      break;
    case 'Back Wall':
      firestoreFieldName = 'backWall';
      targetStack = tradeFairDoc.standSetup.backWall;
      isWallStack = true;
      break;
  }

  print('Checked wall stacks, isWallStack: $isWallStack');

  // If not a wall, check floor stacks and samples
  if (!isWallStack) {
    for (var i = 0; i < tradeFairDoc.standSetup.floorStacks.length; i++) {
      if (tradeFairDoc.standSetup.floorStacks[i].stackPosition == stackName) {
        stackIndex = i;
        targetStack = tradeFairDoc.standSetup.floorStacks[i].stackItems;
        firestoreFieldName = 'floorStacks';
        print('Found stack in floorStacks at index: $stackIndex');
        break;
      }
    }

    if (targetStack == null) {
      for (var i = 0; i < tradeFairDoc.standSetup.samples.length; i++) {
        if (tradeFairDoc.standSetup.samples[i].stackPosition == stackName) {
          stackIndex = i;
          targetStack = tradeFairDoc.standSetup.samples[i].stackItems;
          firestoreFieldName = 'samples';
          print('Found stack in samples at index: $stackIndex');
          break;
        }
      }
    }
  }

  // Check if we found a target stack
  if (targetStack == null ||
      firestoreFieldName.isEmpty ||
      (stackIndex == -1 && !isWallStack)) {
    print('Invalid stackName or unable to find the stack path: $stackName');
    return null;
  }

  print(
      'Target stack found, firestoreFieldName: $firestoreFieldName, stackIndex: $stackIndex');

  String firestoreArrayPath = isWallStack
      ? 'standSetup.$firestoreFieldName'
      : 'standSetup.$firestoreFieldName.$stackIndex.stackItems';

  print('Firestore array path: $firestoreArrayPath');

  // Add or delete logic
  if (!isDelete) {
    // Add logic
    if (targetStack.any((item) => item.rugRef == rugRef)) {
      print('Rug already exists in the stack, doing nothing');
      return targetStack;
    }

    RugStackItemStruct newItem = RugStackItemStruct(
      rugRef: rugRef,
      positionInStack: targetStack.length + 1,
    );
    targetStack.add(newItem);

    await tradeFairDoc.reference.update({
      firestoreArrayPath: targetStack.map((item) => item.toMap()).toList(),
    });
    print('Firestore add successful for $stackName');
  } else {
    // Delete logic
    int itemIndex = targetStack.indexWhere((item) => item.rugRef == rugRef);
    if (itemIndex != -1) {
      targetStack.removeAt(itemIndex);

      // Adjust the positions in the stack
      for (int i = itemIndex; i < targetStack.length; i++) {
        targetStack[i].positionInStack -= 1;
      }

      await tradeFairDoc.reference.update({
        firestoreArrayPath: targetStack.map((item) => item.toMap()).toList(),
      });
      print('Firestore delete successful for $stackName');
    } else {
      print('Rug not found in the stack, doing nothing');
    }
  }

  // Print final floorStack array
  print('Final floorStack: ${tradeFairDoc.standSetup.floorStacks}');

  return targetStack;
}

2