Back to Loopback Next

Include filter

docs/site/Include-filter.md

4.0.0-alpha.17.4 KB
Original Source

An include filter enables you to include results from related models in a query over relations. (See Relations for details on how to define relations)

The value of the include filter can be a string, an array, or an object.

{% include important.html content="You can use an include filter with find(), findOne() and findById() methods. " %}

Node.js API

To query one relation:

ts
{
  include: ['relationName'];
}

or

ts
{
  include: [{relation: 'relationName'}];
}

To query multiple relations:

ts
{
  include: ['relationName1', 'relationName2'];
}

or

ts
{
  include: [{relation: 'relationName1'}, {relation: 'relationName2'}];
}

To query nested relations, use the scope field:

ts
{
    relation: 'relationName',
    scope: {
      include: ['nestedRelationName'],
    },
}

Where:

  • relationName is the name of a relation defined in repositories. Check Relations for details.

REST API

To query one relation: /modelName?filter[include][]=_relationName_ or /modelName?filter[include][][relation]=_relationName_

To query multiple relations: /modelName?filter[include][0]=_relationName1_&filter[include][1]=_relationName2_ or /modelName?filter[include][0][relation]=_relationName1_&filter[include][1][relation]=_relationName2_

To query nested relations, as the url would get too long, we recommend to encode it with encodeURIComponent(JSON.stringify(filter)): /modelName?filter=<encodeResult>

You can also use stringified JSON format in a REST query.

Scope Filter

Please note if the scope filter contains non-string data, they won't be coerced if you use the plain query. Only the stringified JSON format works as expected.

For example, the following REST query which includes related users without their names:

/modelName?filter[include][0][relation]=users&filter[include][0][scope][fields][name]=false

won't hide the name field for users, because false is a boolean value. It won't be coerced and will present as a string when passed to the controller function.

A solution is to use the stringified JSON query instead to include data with scope specified:

ts
// Define the inclusion filter and get its encoded format
const inclusionFilter = {
  include: {
    relation: 'users',
    scope: {
      fields: {name: false},
    },
  },
};
const encodedFilter = encodeURIComponent(JSON.stringify(inclusionFilter));

and call /modelName?filter=<encodedFilter>

Examples

  • Return all customers with their orders:

{% include code-caption.html content="Node.js API" %}

ts
await customerRepository.find({include: ['orders']});

{% include code-caption.html content="REST" %}

/customers?filter[include][]=orders

Or stringified JSON format:

/customers?filter={"include":["orders"]}

Result:

ts
[{
    id: 1,
    name: 'Tom Nook',
    orders:[{id: 1, desc: 'pencil'}]
 },
 {...}
]
  • Return all customers with their orders and their address:

{% include code-caption.html content="Node.js API" %}

ts
await customerRepository.find({
  include: ['orders', 'address'],
});

{% include code-caption.html content="REST" %}

/customers?filter[include][]=orders&filter[include][]=address

Result:

ts
[{
    id: 1,
    name: 'Tom Nook',
    orders:[{id: 1, desc: 'pencil'}],
    address: {'8200 Warden Ave'}
 },
 {...}
]
  • Return all customers with their orders and also the shipment information of orders:

{% include code-caption.html content="Node.js API" %}

ts
await customerRepository.find({
  include: [
    {
      relation: 'orders',
      scope: {
        include: [{relation: 'shipment'}],
      },
    },
  ],
});

{% include code-caption.html content="REST" %}

(using encodeURIComponent)

/customers?filter=%7B"include"%3A%5B%7B"relation"%3A"orders"%2C"scope"%3A%7B"include"%3A%5B%7B"relation"%3A"shipment"%7D%5D%7D%7D%5D%7D

Result:

ts
[{
    id: 1,
    name: 'Tom Nook',
    orders:[
        {id: 123,
        desc: 'pencil',
        shipment: {id: 999, company: 'UPS'} // nested related models
        }
    ],
 },
 {...}
]

Combined use of fields and include for a belongsTo relation

If you want to use both include and fields to display only specific fields of a model and a specific belongsTo relation, you need to add the relation foreign key in the fields :

Return all posts only with field title and the relation category:

ts
await postRepository.find({
  include: [{relation: 'category'}],
  fields: ['title', 'categoryId'],
});

Include with filters

In some cases, you may want to apply filters to related models to be included.

{% include note.html content="

When you apply filters to related models, the query returns results from the first model plus any results from related models with the filter query, similar to a "left join" in SQL.

" %}

LoopBack supports that with the following syntax (for example):

ts
await postRepository.find({
  include: [
    {
      relation: 'owner', // include the owner object
      scope: {
        // further filter the owner object
        fields: ['username', 'email'], // only show two fields
        include: {
          // include orders for the owner
          relation: 'orders',
          scope: {
            where: {orderId: 5}, // only select order with id 5
          },
        },
      },
    },
  ],
});

For real-world scenarios where only users in $authenticated or $owner roles should have access, use findById(). For example, the following example uses filters to perform pagination:

ts
await postRepository.findById('123', {
  include: [
    {
      relation: 'owner',
      scope: {
        // fetch 1st "page" with 5 entries in it
        skip: 0,
        limit: 5,
      },
    },
  ],
});

{% include important.html content="The limit filter will be applied on a per parent record basis. So each parent record will include a max of limit number of records. Previously, we had a bug where limit will be applied globally, so not all parent's records will include related objects depending on the limit value. To keep backward compatibility with this, in the weird case it is needed, you can use totalLimit instead. For more details see here " %}

Access included objects

In the Node.js API, you can simply access the returned model instance with related items as a plain JSON object. For example:

ts
const result = await postRepository.find({
  include: [{relation: 'owner'}, {relation: 'orders'}],
});
console.log(result[0].owner, result[0].orders);
// log the related owner and order of the first returned instance

Note the relation properties such as post.owner reference a JavaScript function for the relation method.

REST examples

These examples assume a customer model with a hasMany relationship to a reviews model.

Return all customers including their reviews:

/customers?filter[include][]=reviews

Return all customers including their reviews and also their orders:

/customers?filter[include][]=reviews&filter[include][]=orders

Return all customers whose age is 21, including their reviews:

/customers?filter[include][]=reviews&filter[where][age]=21

Return first two customers including their reviews:

/customers?filter[include][]=reviews&filter[limit]=2

See also: Querying related models.