Skip to content

Commit

Permalink
non-equal filter predicate (#216)
Browse files Browse the repository at this point in the history
following up on #215, this PR introduces a filter predicate to check for not-equal, including null.

```json
"filter": {
    "my_field": {
        "ne": "my_value"
    }
}
```

```json
"filter": {
    "my_field": {
        "ne": null
    }
}
```

part of https://issues.redhat.com/browse/APPSRE-8502
  • Loading branch information
geoberle authored Jan 25, 2024
1 parent e14920f commit ecad288
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 1 deletion.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,36 @@ To filter on an fields value, use the following filter object syntax

This way only resources with such a field and value are returned by the query.

Also works for null.

```json
"filter": {
"my_field": null
}
```

### Field not-equal predicate

To check if a property value is not equal to a certain value, use the `ne` filter predicate

```json
"filter": {
"my_field": {
"ne": "my_value"
}
}
```

This also works to check for missing values or references.

```json
"filter": {
"my_field": {
"ne": null
}
}
```

### List contains predicate

Field values can be also compared towards a list of acceptable values.
Expand Down
14 changes: 13 additions & 1 deletion src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ const fieldEqPredicate = (field: string, value: any, source: any): boolean => (
(source[field] ?? null) === value
);

const fieldNotEqualPredicate = (field: string, value: any, source: any): boolean => {
const sourceValue = (source[field] ?? null);
return value !== sourceValue;
};

const arrayEqPredicate = (field: string, comparisonArray: any, source: any): boolean => {
const sourceArray = (source[field] ?? null);
if (sourceArray === comparisonArray) {
Expand Down Expand Up @@ -150,13 +155,17 @@ const getFilters = (
name: string,
): FilterDict => app.get('searchableFields')[bundleSha][name];

const SUPPORTED_FILTER_OPERATORS = ['in', 'filter', 'ne'];

const isConditionsObject = (
conditionsObject: any,
): boolean => {
if (conditionsObject == null) {
return false;
}
return Object.prototype.hasOwnProperty.call(conditionsObject, 'in') || Object.prototype.hasOwnProperty.call(conditionsObject, 'filter');
return SUPPORTED_FILTER_OPERATORS.some((operator) => (
Object.prototype.hasOwnProperty.call(conditionsObject, operator)
));
};

const filterPredicate = (
Expand Down Expand Up @@ -187,6 +196,9 @@ const conditionsObjectPredicate = (
if (Object.prototype.hasOwnProperty.call(value, 'filter')) {
return filterPredicate(field, value.filter, fieldGqlType, app, bundleSha, source);
}
if (Object.prototype.hasOwnProperty.call(value, 'ne')) {
return fieldNotEqualPredicate(field, value.ne, source);
}
throw new GraphQLError(
`Condition object ${value} unsupported`,
);
Expand Down
44 changes: 44 additions & 0 deletions test/filter/filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import * as db from '../../src/db';
chai.use(chaiHttp);
chai.should();

const should = chai.should();

describe('pathobject', async () => {
let srv: http.Server;
before(async () => {
Expand Down Expand Up @@ -134,6 +136,48 @@ describe('pathobject', async () => {
resp.body.data.test[0].name.should.equal('resource D');
});

it('filter object - non-null field value', async () => {
const query = `
{
test: resources_v1(filter: {reference: { ne: null }}) {
name
reference {
name
}
}
}
`;
const resp = await chai.request(srv)
.post('/graphql')
.set('content-type', 'application/json')
.send({ query });
resp.should.have.status(200);
resp.body.data.test.length.should.be.above(0);
resp.body.data.test.forEach((r: { reference?: any; }) => {
should.exist(r.reference);
});
});

it('filter object - not-equal field value', async () => {
const query = `
{
test: resources_v1(filter: {optional_field: { ne: "E" }}) {
name
optional_field
}
}
`;
const resp = await chai.request(srv)
.post('/graphql')
.set('content-type', 'application/json')
.send({ query });
resp.should.have.status(200);
resp.body.data.test.length.should.be.above(0);
resp.body.data.test.forEach((r: { optional_field?: string; }) => {
should.not.equal(r.optional_field, 'E');
});
});

it('filter object - list field eq', async () => {
const query = `
{
Expand Down

0 comments on commit ecad288

Please sign in to comment.