简体   繁体   中英

Querying many-to-many relationship: get all products with tag by name

This should actually be pretty easy to do, but I have not managed....

Let's say we have a model Product . A Product can have multiple Tag . The relationship is defined like this:

common/models/product.json

{
  "name": "Product",
  #other stuff
  "relations": {
      "tags": {
      "type": "hasMany",
      "model": "Tag",
      "foreignKey": "productId",
      "through": "ProductTags"
    }
  },
  #more stuff
}

common/models/product-tags.json

{
  "name": "ProductTags",
  #more stuff
  "relations": {
    "product": {
      "type": "belongsTo",
      "model": "Product",
      "foreignKey": "productId"
    },  
    "tag": {
      "type": "belongsTo",
      "model": "Tag",
      "foreignKey": "tagId"
    }   
  },  
  #more stuff
}

common/models/tag.json

  {
      "name": "Tag",
      #more stuff
       "properties": {
         "name": {
         "type": "string",
         "required": true,
         "index": {
         "unique": true
       }
      },
      "relations": {
        "pictures": {
          "type": "hasMany",
          "model": "Pictures",
          "foreignKey": "tagId",
          "through": "PictureTags"
        }
        "products": {
          "type": "hasMany",
          "model": "Product",
          "foreignKey": "tagId",
          "through": "ProductTags"
        }
      },
      #more stuff
    }

How do I query for all products by tag name? Eg get all products which have a tag name "XYZ" (if we could use "like", even better, so that the query is not on exact match, but on a "like"). REST query format preferred.

I've tried looking at: https://docs.strongloop.com/display/public/LB/Include+filter But my include filter would first return all products,and add tags information for them.

EDIT: A few tries:

curl --globoff http://localhost:3000/api/v1/Tags?filter[where][name][inq]=FirstTag&filter[where][name][inq]=ThirdTag&filter[include][products]

This one is straight from the docs returns an error:

The name property has invalid clause {\\"inq\\":\\"FirstTag\\"}\\

curl --globoff http://localhost:3000/api/v1/Products?filter=[include][tags][name]=NonExistingTag

which returns all products nonetheless

curl -X GET "http://localhost:3000/api/v1/Products?filter[where]tags][name]=First" --globoff

returns this error: { "error": { "name": "TypeError", "status": 500, "message": "Cannot read property 'type' of undefined", "stack": "TypeError: Cannot read property 'type' of undefined\\n at PostgreSQL.toColumnValue (/home/fabio/prj/fapl/src/loopback/node_modules/loopback-connector-postgresql/lib/postgresql.js:432:11)\\n

Also I looked at: https://docs.strongloop.com/display/public/LB/Where+filter But couldn't find any relevant example...the where clause is for the products objects themselves, not for a related model.

If your models are set up correctly you should be able to do something like

//...snip...
Tag.findById(tagId, function(err, tag){

  // works for multiple relation types
  // hasMany, hasAndBelongsToMany, etc
  tag.products({}, function(err, productsWithTag) {
    if(err) return cb(err);
    // productsWithTag available here
  });

});
//...snip...

Which should return all the products for that tag. findById could be replaced with a find(filter...) instead to get at the tag instance by name or query.

See https://docs.strongloop.com/display/public/LB/HasAndBelongsToMany+relations at the bottom for "methods added to the model" for guidance. Also check that your model json is correct and figure out if it's hasAndBelongsToMany or a combination of hasMany and belongsTo that works best.

I asked this question on the loopbackjs google group as well.

Someone (Nuba Princigalli, if you are here you should post your solution as answer, so that I can accept it...) posted this answer:

Assuming your tags live at /api/tags, just make a HTTP GET request at /api/tags?filter={"where":{"id":"1234"}, "include":"products"}" customizing the where clause to fit your needs with a like or a regex.

If more than one tag is returned, each will have its own list of products. If you want just a single product list, you could merge them on the client, or do the query in two steps: find all tags matching your where filter, including their related ProductTags, then use those to build a list of product ids you'll want, then find all products using the inq operator. https://docs.strongloop.com/display/public/LB/Where+filter#Wherefilter-inq . No custom endpoint needed.

It's somehow not intuitive to have to query tags in order to get products. Also, if a product is assigned multiple tags, and I am querying multiple tags, I would get the same product multiple times.

Not ideal but this is ok for the time being for us!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM