1

I have this collection. I am trying to add object(item = {label: "Chrome", value: "chrome"}) to the array only if the value is unique, i.e. insert the whole object, item in the array foo only if array foo doesn't have any other object with the same value as item, using one MongoDB operation

foo=[{label:"IE",value:"ie"},{label:"Firefox",value:"firefox"}]
Ronit Roy
  • 890
  • 1
  • 8
  • 10

1 Answers1

2

i don't think $addToSet supports duplicate detection of object fields like you want. however you could do it like this:

db.bars.update(
    {
        "_id": ObjectId("5d3421a6a0100c1e083356e1"),
        "foo": {
            "$not": {
                "$elemMatch": {
                    "value": "firefox"
                }
            }
        }
    },
    {
        "$push": {
            "foo": {
                "label": "Fake Fox",
                "value": "firefox"
            }
        }
    }
)

first you match the parent object by id + that it doesn't contain "firefox" as the value in the foo object array. then you specify a $push to add your new object to the foo array. that way, no duplicates will be created in the foo array.

not sure what your coding language is but here's the c# code that generated the above mongo query in case anybody's interested:

using MongoDB.Entities;
using System.Linq;

namespace StackOverflow
{
    public class Program
    {
        public class bar : Entity
        {
            public item[] foo { get; set; }
        }

        public class item
        {
            public string label { get; set; }
            public string value { get; set; }
        }

        private static void Main(string[] args)
        {
            new DB("test");

            var bar = new bar
            {
                foo = new[]
                {
                    new item { label = "IE", value = "ie"},
                    new item { label = "FireFox", value = "firefox" }
                }
            };
            bar.Save();

            var chrome = new item { label = "Chrome", value = "chrome" };
            var firefox = new item { label = "Fake Fox", value = "firefox" };

            DB.Update<bar>()
              .Match(b => 
                     b.ID == bar.ID && !b.foo.Any(i => i.value == chrome.value))
              .Modify(x => x.Push(b => b.foo, chrome))
              .Execute();

            DB.Update<bar>()
              .Match(b => b.ID == bar.ID && !b.foo.Any(i => i.value == firefox.value))
              .Modify(x => x.Push(b => b.foo, firefox))
              .Execute();
        }
    }
}
Dĵ ΝιΓΞΗΛψΚ
  • 5,068
  • 3
  • 13
  • 26
  • but the array `foo` is stored in the database, so we can't simply use `"$elemMatch": { "value": "firefox" }` because we don't know the value is firefox or something else. – Ronit Roy Jul 22 '19 at 01:19
  • the coding language used is javascript(NodeJs) – Ronit Roy Jul 22 '19 at 01:20
  • if you look at the c# code, the object `{ label = "FireFox", value = "firefox" }` is already in the db. then we try to add a new object `{ label = "Fake Fox", value = "firefox" }`. but the new object was not added to the foo array because the label already existed in the db. i thought that was what you wanted to do? – Dĵ ΝιΓΞΗΛψΚ Jul 22 '19 at 01:56