Search code examples
javascriptangularjsnode.jsloopbackjsstrongloop

Strongloop Loopback: Filter by id of related Model


I have a Strongloop Loopback Node.js project with some models and relations.

The problem at hand

My problem relates how to query only those Media instances that have a relation to a certain Tag id, using the Angular SDK - while not querying Tags.media (which return Tag instances), but instead making a query somehow that returns plain Media instances.

Please read below for specific information..

Spec

Basically, I have a Model Media which has many 'tags' (model Tag). Think of a image file (Media) having various EXIF tags (Tag). Here is the relation spec (this all works as expected):

Media (media.json):

{
  "name": "media",
  "base": "PersistedModel",
  "properties": {
    "id": {
      "type": "string",
      "id": true
    }
  },
  "relations": {
    "tags": {
      "type": "hasAndBelongsToMany",
      "model": "tag"
    }
}

Tag (tag.json):

{
  "name": "tag",
  "base": "PersistedModel",
  "idInjection": true,
  "properties": {
    "name": {
      "type": "string",
      "required": true
    }
  },
  "relations": {
    "medias": {
      "type": "hasAndBelongsToMany",
      "model": "media"
    }
  },
  "acls": [],
  "methods": []
}

Solutions

Now, I know I could do a query like this (using Angular SDK in my example, but the syntax is the same):

injector.get('Tag').find({
  'filter': {
    'include': 'medias',
    'where': {'id': <mytagid>}
  }
});

My problem with this approach is, that I receive 1 (one) Tag instance with attached Media instances. This disrupts why whole workflow as I deal only with Media instances.. i just want to filter by Tag id, not bother about Tag at all.

Bottom line

If I see the API explorer (/explorer/), the return value of GET /api/tags/<myTagID>/medias is exactly what I need - an array of Media objects - but how to query them exactly like this using the Angular SDK (lb_services)?


Solution

  • I had a similar problem. One recommendation is to open the lb-services.js and try to find: /tags/:id/medias or something similar. Then you will find a comment like this: // INTERNAL. Use Tags.medias() instead. Or something similar. So that is the method that you should call. Do not call the "prototype$__get....." methods.

    Then just call what it says there I suppose: Tag.medias({id:})

    Other suggestions:

    As you said in your description Media has many Tags. So why not use just

    {
      "name": "media",
      "base": "PersistedModel",
      "properties": {
        "id": {
          "type": "string",
          "id": true
        }
      },
      "relations": {
        "tags": {
          "type": "hasMany",   <---------- hasMany
          "model": "tag",
          "foreignKey": "tagId" <---FK name
        }
      }
    

    and for the tags just belongsTo as type.

    {
      "name": "tag",
      "base": "PersistedModel",
      "idInjection": true,
      "properties": {
        "name": {
          "type": "string",
          "required": true
        }
      },
      "relations": {
        "medias": {
          "type": "belongsTo",
          "model": "media",
          "foreignKey": "mediaId"  <---FK name
        }
      },
      "acls": [],
      "methods": []
    }
    

    But really I don't think this is the problem because you said when you request GET /api/tags/<myTagID>/medias it returns what you want.

    Then, in AngularJS you can use:

     Media.tags({id:<mediaId>})
    

    for media/:id/tags

    and for the other side try:

    Tag.medias({id:<tagId>})
    
    
    Tag.find({
           filter:{
              where:{mediaId: <mediaId>}     <----mediaId comes from FK name    
           }
       })
    

    In this case both are persistent models there is no problems, I had permission problems when doing a similar thing with data that extends User type. But that is another story...

    Hope this is helpful, I changed some stuff from a similar app that I am doing and hope not making so many errors when adapting to your code...