Search code examples
mongodbapistrapi

How Can I send array tag Id's in Strapi using AND logic not OR logic between them? [MongoDB]


I have an array of IDs that are populated in an array.

I am trying to send an API get request to Strapi with filter as per below:

  let listTagId = [];
  tagOptions.map((tag) => {
    if (inputValues[`${tag.nameTag}`]) {
      listTagId.push(tag.id);
    }
  });

  let filter = {
    ...cleanObject({
        'tags.id': listTagId.length > 0 ? listTagId : null,
     }

Where cleanObject equals

function cleanObject(obj) {
  let propNames = Object.getOwnPropertyNames(obj);
  for (let i = 0; i < propNames.length; i++) {
    var propName = propNames[i];
    if (obj[propName] === null || obj[propName] === undefined) {
      delete obj[propName];
    }
  }
  return obj;
}

The filter then is used later with axios which is calling to Strapi

  async FIND(filter) {
    try {
      let query
      if (filter) {
        query = qs.stringify(filter)
      }
      let endpoint = `${this.apiEnpoint}${this.service}${query ? `?${query}` : ''}`
      let { data } = await axios.get(endpoint,
        {
          headers: this.headers(this.getToken())
        })
      return { data }
    } catch (error) {
      return { error: this.getErrorMessage(error) }
    }
  }

The Tags are a collection which has a relationship to the API-OBJECT. What I would want to achieve is to filter only those API-OBJECTs which have all of the tags.id included (tag.id1 and tag.id2...).

At the moment if I use the logic as per above (code) it uses OR logic, meaning it would retrieve back the results from the server if an object has ONE of the tags is in the listTagId.

Example:

  1. Sending listTagId =['1223', '345'], currently it retrieves all objects with id's '1223' or '345'. So The object could have a Tag relationship with the 1223 tag only and it would be still retreived. Incorrect behaviour for me.
  2. Sending listTagId =['1223', '345'], the call should only retrieve the objects having tags with id's '1223' AND '345'.

Extract from the console:

'GET /api-end-point/count?_where%5Bcategory_contains%5D=houses&_where%5Btags.id%5D%5B0%5D=60420fc8c4bed44584638dec&_where%5Btags.id%5D%5B1%5D=60420febc4bed44584638ded&_where%5Btags.id%5D%5B2%5D=6042151fbea9601908f2c774&_where%5Btags.id%5D%5B3%5D=60421520bea9601908f2c775&_where%5Btags.id%5D%5B4%5D=60421521bea9601908f2c776&_where%5Btags.id%5D%5B5%5D=60421522bea9601908f2c777&_where%5Btags.id%5D%5B6%5D=60421522bea9601908f2c778 (562 ms)' 

I have tried to use tags.id_in, but this gave the same results as now.

How should I change the query for the 'tags.id' in order to retrieve the desired elements?


Solution

  • I managed to go around this, by customizing the controller as it turns out Strapi does not have an AND logic as I found. Though I don't like that this will use a lot of recourses as arrays are being updated and looped through fairly many times. If there are other suggestions please let me know.

    What I am doing here is remove the limit so that I get all entities and then:

    1. Getting all entities that have all the tags (using OR), and having the rest of the filters.

    2. Once the size is reduced by something I am looping through the elements and finding the ones which have all of the tags ids.

    3. I don't want to go through each and single one, so once I reached the limit I break out of the foreach loop.

      "use strict";

      const { sanitizeEntity } = require("strapi-utils");

      module.exports = { async find(ctx) { let tags; let entities; let tagEntities = []; let limit; if (ctx.query?._limit) { limit = ctx.query._limit; delete ctx.query._limit; }

       if (ctx.query?._where?.tags) {
         tags = ctx.query._where.tags;
         delete ctx.query._where.tags;
         ctx.query._where["tags.id_in"] = tags;
       }
      
       if (ctx.query._q) {
         entities = await strapi.services["custom-controller"].search(ctx.query);
       } else {
         entities = await strapi.services["custom-controller"].find(ctx.query);
       }
      
       tagEntities = [];
       try {
         entities.forEach((element) => {
           if (limit <= 0) throw BreakException;
      
           let newListId = [];
           element.tags.forEach((tag) => newListId.push(tag?.id));
      
           var result = tags?.every((val) => {
             return newListId.indexOf(val) >= 0;
           });
      
           if (!tags) {
             tagEntities.push(element);
             limit--;
           }
      
           if (result) {
             tagEntities.push(element);
             limit--;
           }
         });
       } catch (e) {}
      
       return tagEntities.map((entity) =>
         sanitizeEntity(entity, { model: strapi.models["custom-controller"] })
         );
        },
      
        };