1

My question is similar to this question asked in 2016 which unfortunately does not have an accepted answer. I'm also unable to use the answer there to figure out how I need to accomplish the same.

I am basically trying to add a new item to an array in my data model.

My current data model (What I want) is like this:

{
   teamId: 'team32',
   teamName: 'Lions',
   players: ['Jack','Ryan','Sam']
}

When a new team is created, an empty array is added for 'players'. There is no model for players. It is just an array which can hold a list of players.

How do I add a new player to the list of players (Add a string to the list)? I'm also open to storing it as an object instead of string.

My Team model

{
  "name": "teams",
  "base": "PersistedModel",
  "idInjection": true,
  "options": {
    "validateUpsert": true
  },
  "properties": {
    "teamID": {
      "type": "string",
      "required": true
    },
    "teamName": {
      "type": "string",
      "required": true
    },
    "players":{
      "type":"array",
    }
  },
  "validations": [],
  "relations": {},
  "acls": [],
  "methods": {}
}

team.js

var updateQuery = {players:{$addToSet:{'Mark'}}};

team.update({teamID:'teams32'},updateQuery,
     function(err,data){
         console.log(err,data,'result');
      });

$addToSet and $push have both worked for me with MongoDB with mongoose. But somehow don't seem to work on loopback. How do I go about this since $addToSet and $push are both not working for me in this case?

2
  • Try this team.update( { "teamID": "teams32" }, { "$addToSet": { "properties.players": "Mark" }}, ) Commented Aug 6, 2018 at 10:09
  • @AnthonyWinzlet I tried this out. But same result. {"$addToSet":{"properties.players":"Mark"}} objects gets added to the players field, instead of adding to the list. I am also confused at how the datatype switches from array to object after this operation. Commented Aug 7, 2018 at 7:39

4 Answers 4

3

You should extend your model capabilities to use those operators. Something like:

"options": {    
    "mongodb": {
      "collection": "model_collection",
      "allowExtendedOperators": true
    }
  },

As stated in loopback documentation: https://loopback.io/doc/en/lb2/MongoDB-connector.html

Then you can just use it like:

team.update({teamID:'teams32'},{ $addToSet:{ players: 'Mark'}},
     function(err,data){
         console.log(err,data,'result');
      });
Sign up to request clarification or add additional context in comments.

1 Comment

im guessing loopback has no support for addToSet on the client side via the SDK
1

This deserves two calls but might help you:

team.addPlayer = function (player, cb) {
    team.findOne({where: {teamID: "teams32"}}, function (err, team) {
        if (!err && team) {
            var players = [];
            if (team.players) {
                players = team.players;
            }
            players.push(player);
            team.updateAttributes({players: players}, function(err, team) {
                cb (err, team);
            });
        } else {
            cb (err, {});
        }
    });
};

team.remoteMethod('addPlayer', {
    accepts: {arg: 'player', type: 'string', required: true},
    returns: {arg: 'team', type: 'object'},
    http: {verb: 'put'}
});

5 Comments

This is a good answer. I decided to go with this approach since I could not find any other way. But this involves querying the database twice. I would prefer to do it with a single query to reduce the amount of work done on the application level and also to reduce the number of queries. Is this not possible with loopback?
I agree, but I'm not seeing anything else. Have you tried what @ Anthony Winzlet has suggested in his comment?
Yup I tried that as well. But didnt work for me. It is rather amusing because I have used both $push and $addToSet successfully with Mongoose on ExpressJS. But it simply doesnt work with Loopback.
is there any better solution than this approach ?
I stopped developping on LoopbackJs - but did you try @Ricardo Pinto 's answer?
0

As of my knowledge the find, update and any other functions here in loopback is not same as MongoDB native query. It has their own customizations. So team.update is not same as db.team.update(). So, Either you can use 2 db queries as said in the previous answer or you can try by running native query by getting the datasource. Then you can use $addToSet easily and it will do the job in a single hit.

team.getDataSource().connector.connect(function (err, db) {
    var collection = db.collection("team");
    collection.update(filter,{$addToSet: {players:{$addToSet:{'Mark'}}}}, function(err,res){ /*Anything you want to do here*/ })
})

This may help.

Comments

0

I have just implemented this endpoint with node --version v10.16.0 and [email protected]

With it I can append values to any field in my model (only handling array type field currently). the data param should look like [ "one", 2, true ]

    Dataset.appendToArrayField = function (id, fieldName, data, ctx, next) {
    const where = {pid: id};
    var $addToSet = {};
    // $each is necessary as data is an array of values
    // $addToSetis necessary to append to the field and not overwrite
    $addToSet[fieldName] = { $each: data };
    Dataset.update(where,  { $addToSet });
    next();
};

Dataset.remoteMethod("appendToArrayField", {
    accepts: [{
            arg: "id",
            type: "string",
            required: true,
            description: ""
        },
        {
            arg: "fieldName",
            type: "string",
            required: true,
            description: "Name of field to append data to"
        },
        {
            arg: "data",
            type: "array",
            required: true,
            description: "An array of values to append"
        },
        {
            arg: 'options',
            type: 'object',
            http: {
                source: 'context'
            }
        }
    ],
    http: {
        path: "/appendToArrayField",
        verb: "post"
    },
    returns: {
        type: "Object",
        root: true
    },
    description: "updates a single record by appending data to the specified field"
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.