Conditional data update shows error

I’m using MongoDB with NodeJS. I have a user database like the one below:

db.user.insertOne({
  email: "user2@email.com",
  name: "First Last",
  age: 55,
analytics: [   { date: "2023-03-25", country: { UK: 1 }, browser: {}, os: {} },
  { date: "2023-03-26", country: {}, browser: {}, os: {} },
  { date: "2023-03-27", country: {}, browser: {}, os: {} },
  { date: "2023-03-28", country: {}, browser: {}, os: {} },
  { date: "2023-03-29", country: { UK: 1, AU: 5 }, browser: {}, os: {} },]}
);

Here is what I want to do:
I want to do a findOneAndUpdate query. If today’s date exists in the analytics array, it will update the country, browser, and os field data. If the date does not exist inside the analytics array then it will create a new object like { date: "2023-03-25", country: { UK: 1 }, browser: { Opera: 1 }, os: { Windows: 1 } }

I tried doing queries like these, but it shows Updating the path 'analytics.$[element].country.UK' would create a conflict at 'analytics'

db.user.findOneAndUpdate(
   { email: "user2@email.com" },
   {
      $set: { "analytics.$[element].date": "2023-03-31", "analytics.$[element].country": {"UK": 1}, "analytics.$[element].browser": {"Opera": 1}, "analytics.$[element].os" : {"Windows": 1} },
      $inc: { "analytics.$[element].country.UK": 1, "analytics.$[element].browser.Opera" : 1, "analytics.$[element].os.Windows" : 1 }
   },
   {
      arrayFilters: [{ "element.date": "2023-03-30" }],
      upsert: true,
      returnOriginal: false
   }
)

Is there any way to conditionally update this in a single findOneAndUpdate query or using other methods?

Hi :wave: @Ahsan,

Welcome to the MongoDB Community forums :sparkles:

Based on your shared sample documents and the update condition I’ve written an update query. Sharing the code snippet for your reference:

import mongodb from "mongodb";
...
  try {
    await client.connect();
    const collection = client.db('test').collection('sample');

    // Test case 1: Update existing document
    const result = await collection.findOneAndUpdate(
      {
        "email": "user1@email.com",
        "analytics.date": "2023-03-29"
      },
      {
        $set: {
          "analytics.$[element].country.UK": 1,
          "analytics.$[element].browser.Opera": 1,
          "analytics.$[element].os.Windows": 1
        }
      },
      {
        arrayFilters: [{ "element.date": "2023-03-29" }],
      }
    );
    console.log("Document updated:", result.value);

    // Test case 2: Insert a new document
    const result2 = await collection.findOneAndUpdate(
      {
        "email": "user2@email.com",
        "analytics.date": "2023-03-30"
      },
      {
        $set: {
          "analytics.$[element].country.UK": 1,
          "analytics.$[element].browser.Opera": 1,
          "analytics.$[element].os.Windows": 1
        }
      },
      {
        arrayFilters: [{ "element.date": "2023-03-30" }],
      }
    );

    if (!result2.value) {
      const result3 = await collection.findOneAndUpdate(
        {
          "email": "user2@email.com"
        },
        {
          $push: {
            "analytics": {
              "date": "2023-03-30",
              "country": {
                "UK": 1
              },
              "browser": {
                "Opera": 1
              },
              "os": {
                "Windows": 1
              }
            }
          }
        },
        {
          upsert: true,
        }
      );
      console.log("Document inserted:", result3.value);
    }
  } catch (err) {
    console.error(err);
  } finally {
    await client.close();
  }
}

The code uses the findOneAndUpdate method to update/insert a document in the user collection based on the email and date of the analytics. If a document exists, the analytics object is updated with new data using the $set operator and the arrayFilters option. If no document exists, a new document is inserted with the $push operator and the upsert option set to true.

Please note this is just an example solution code and you should update this as per your requirements. It is recommended to evaluate the performance and test this with expected workloads based on your specific use case.

I hope it helps!

Best,
Kushagra