dxfrontier / cds-ts-repository

SAP CAP TypeScript CDS-QL Repository. Simplified interface for common database actions.
8 stars 2 forks source link

[FEATURE] Ability to supply multiple filters to `find()` and `findOne()` methods #79

Open hakimio opened 2 months ago

hakimio commented 2 months ago

Description

Right now it seems it's only possible to supply a single instance of Filter to find() and findOne() method. It would be great if we could supply an array of Filter instances.

Suggested solution

Following simple case assumes and relation for filters:

const results = await this.builder().find([filter1, filter2]).execute();

or operator can also be used:

const results = await this.builder().find([filter1, 'or', filter2]).execute();

Nested arrays can be used to group filters:

const results = await this.builder()
    .find([
        [filter1, 'or', filter2],
        'and',
        [filter3, 'or', filter4]
    ])
    .execute();

Side note

You can find how this can be translated to query-by-example object on capire.

dragolea commented 2 months ago

Hi, Is this something that might help ? Here you cam combine multiple filters in one filter, there are 2 overloads for Filter.

Example 2 : Combination of 2...n filters

import { MyEntity } from 'LOCATION_OF_YOUR_ENTITY_TYPE';
import { Filter, BaseRepository } from '@dxfrontier/cds-ts-repository';

class MyRepository extends BaseRepository<MyEntity> {
  constructor() {
    super(MyEntity); // a CDS Typer entity type
  }

  public async aMethod() {
    const filterLike = new Filter<MyEntity>({
      field: 'customer_name',
      operator: 'LIKE',
      value: 'abs',
    });

    const filterBetween = new Filter<MyEntity>({
      field: 'stock',
      operator: 'BETWEEN',
      value1: 11,
      value2: 333,
    });

    const filterIn = new Filter<MyEntity>({
      field: 'ID',
      operator: 'IN',
      value: [201, 203, 207],
    });

    /*
    combinedLikeAndBetweenFilters translates to : 
    customer_name like 'abs' or stock between 11 and 333
    */
    const combinedLikeAndBetweenFilters = new Filter('OR', filterLike, filterBetween);

    /* 
    filters translates to : 
    (customer_name LIKE 'abs' OR stock BETWEEN 11 and 333) AND ID IN ('203', '201', '207')
    */
    const filters = new Filter('AND', combinedLikeAndBetweenFilters, filterIn);

    // Execute the query using the builder find
    const results = await this.builder().find(filters).getExpand('orders').execute();
    // OR
    // Execute the query using the .find
    const results2 = await this.find(filters);
  }
}
hakimio commented 2 months ago

While your suggested solution would solve the issue, I still think my proposed solution has much better DX. To be honest, I find it hard to follow the logic in your code.

hakimio commented 2 months ago

My solution was inspired by DevExtreme UI library: DevExtreme DataSource filter.

dragolea commented 2 months ago

Your proposal looks quite good, In practice we will have a multi dimensional array which has some sort of tuples inside [filter1, 'or / and', filter2, 'or', filterN, ...] then logical 'or / and' and again N tuple and again N logical or ...

.find([
        [filter1, 'or', filter2],
        'and',
        [filter3, 'or', filter4]
    ])

Let's keep it here and when I have time I can have a look.

hakimio commented 2 months ago

No rush. Basically, to implement this you would have to convert proposed nested filter array to sap query-by-example nested object. Shouldn't be too complicated.