Coder Social home page Coder Social logo

graphile-contrib / postgraphile-plugin-connection-filter Goto Github PK

View Code? Open in Web Editor NEW
279.0 10.0 29.0 2.36 MB

Filtering on PostGraphile connections

License: MIT License

JavaScript 0.50% PLpgSQL 7.54% TypeScript 91.96%
graphile graphile-build postgraphile postgraphql plugin filter

postgraphile-plugin-connection-filter's Introduction

Package on npm

postgraphile-plugin-connection-filter

Adds a powerful suite of filtering capabilities to a PostGraphile schema.

Warning: Use of this plugin with the default options may make it astoundingly trivial for a malicious actor (or a well-intentioned application that generates complex GraphQL queries) to overwhelm your database with expensive queries. See the Performance and Security section below for details.

Usage

Requires PostGraphile v4.5.0 or higher.

Install with:

yarn add postgraphile postgraphile-plugin-connection-filter

CLI usage via --append-plugins:

postgraphile --append-plugins postgraphile-plugin-connection-filter -c postgres://localhost/my_db ...

Library usage via appendPlugins:

import ConnectionFilterPlugin from "postgraphile-plugin-connection-filter";
// or: const ConnectionFilterPlugin = require("postgraphile-plugin-connection-filter");

const middleware = postgraphile(DATABASE_URL, SCHEMAS, {
  appendPlugins: [ConnectionFilterPlugin],
});

Performance and Security

By default, this plugin:

  • Exposes a large number of filter operators, including some that can perform expensive pattern matching.
  • Allows filtering on computed columns, which can result in expensive operations.
  • Allows filtering on functions that return setof, which can result in expensive operations.
  • Allows filtering on List fields (Postgres arrays), which can result in expensive operations.

To protect your server, you can:

  • Use the connectionFilterAllowedFieldTypes and connectionFilterAllowedOperators options to limit the filterable fields and operators exposed through GraphQL.
  • Set connectionFilterComputedColumns: false to prevent filtering on computed columns.
  • Set connectionFilterSetofFunctions: false to prevent filtering on functions that return setof.
  • Set connectionFilterArrays: false to prevent filtering on List fields (Postgres arrays).

Also see the Production Considerations page of the official PostGraphile docs, which discusses query whitelisting.

Features

This plugin supports filtering on almost all PostgreSQL types, including complex types such as domains, ranges, arrays, and composite types. For details on the specific operators supported for each type, see docs/operators.md.

See also:

Handling null and empty objects

By default, this plugin will throw an error when null literals or empty objects ({}) are included in filter input objects. This prevents queries with ambiguous semantics such as filter: { field: null } and filter: { field: { equalTo: null } } from returning unexpected results. For background on this decision, see #58.

To allow null and {} in inputs, use the connectionFilterAllowNullInput and connectionFilterAllowEmptyObjectInput options documented under Plugin Options. Please note that even with connectionFilterAllowNullInput enabled, null is never interpreted as a SQL NULL; fields with null values are simply ignored when resolving the query.

Plugin Options

When using PostGraphile as a library, the following plugin options can be passed via graphileBuildOptions:

connectionFilterAllowedOperators

Restrict filtering to specific operators:

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterAllowedOperators: [
      "isNull",
      "equalTo",
      "notEqualTo",
      "distinctFrom",
      "notDistinctFrom",
      "lessThan",
      "lessThanOrEqualTo",
      "greaterThan",
      "greaterThanOrEqualTo",
      "in",
      "notIn",
    ],
  },
});

connectionFilterAllowedFieldTypes

Restrict filtering to specific field types:

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterAllowedFieldTypes: ["String", "Int"],
  },
});

The available field types will depend on your database schema.

connectionFilterArrays

Enable/disable filtering on PostgreSQL arrays:

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterArrays: false, // default: true
  },
});

connectionFilterComputedColumns

Enable/disable filtering by computed columns:

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterComputedColumns: false, // default: true
  },
});

Consider setting this to false and using @filterable smart comments to selectively enable filtering:

create function app_public.foo_computed(foo app_public.foo)
  returns ... as $$ ... $$ language sql stable;

comment on function app_public.foo_computed(foo app_public.foo) is E'@filterable';

connectionFilterOperatorNames

Use alternative names (e.g. eq, ne) for operators:

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterOperatorNames: {
      equalTo: "eq",
      notEqualTo: "ne",
    },
  },
});

connectionFilterRelations

Enable/disable filtering on related fields:

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterRelations: true, // default: false
  },
});

connectionFilterSetofFunctions

Enable/disable filtering on functions that return setof:

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterSetofFunctions: false, // default: true
  },
});

Consider setting this to false and using @filterable smart comments to selectively enable filtering:

create function app_public.some_foos()
  returns setof ... as $$ ... $$ language sql stable;

comment on function app_public.some_foos() is E'@filterable';

connectionFilterLogicalOperators

Enable/disable filtering with logical operators (and/or/not):

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterLogicalOperators: false, // default: true
  },
});

connectionFilterAllowNullInput

Allow/forbid null literals in input:

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterAllowNullInput: true, // default: false
  },
});

When false, passing null as a field value will throw an error. When true, passing null as a field value is equivalent to omitting the field.

connectionFilterAllowEmptyObjectInput

Allow/forbid empty objects ({}) in input:

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterAllowEmptyObjectInput: true, // default: false
  },
});

When false, passing {} as a field value will throw an error. When true, passing {} as a field value is equivalent to omitting the field.

connectionFilterUseListInflectors

When building the "many" relationship filters, if this option is set true then we will use the "list" field names rather than the "connection" field names when naming the fields in the filter input. This would be desired if you have simpleCollection set to "only" or "both" and you've simplified your inflection to omit the -list suffix, e.g. using @graphile-contrib/pg-simplify-inflector's pgOmitListSuffix option. Use this if you see Connection added to your filter field names.

postgraphile(pgConfig, schema, {
  graphileBuildOptions: {
    connectionFilterUseListInflectors: true, // default: false
  },
});

Examples

query {
  allPosts(filter: {
    createdAt: { greaterThan: "2021-01-01" }
  }) {
    ...
  }
}

For an extensive set of examples, see docs/examples.md.

Development

To establish a test environment, create an empty PostgreSQL database with C collation (required for consistent ordering of strings) and set a TEST_DATABASE_URL environment variable with your database connection string.

createdb graphile_test_c --template template0 --lc-collate C
export TEST_DATABASE_URL=postgres://localhost:5432/graphile_test_c
yarn
yarn test

postgraphile-plugin-connection-filter's People

Contributors

benjie avatar dependabot[bot] avatar edqallen avatar jrmdayn avatar madtibo avatar mattbretl avatar mlipscombe avatar stlbucket avatar syndesis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

postgraphile-plugin-connection-filter's Issues

"or" filter does not work as expected

query {
  allClients(filter: { sex: { in: [FEMALE] }, or:{ sex: { isNull: true }} }) {
    nodes {
      id
      firstName
    }
  }
}

produce

  graphile-build-pg:sql with __local_0__ as (
  graphile-build-pg:sql       select to_json(json_build_array(__local_1__."id")) as "__identifiers", to_json((__local_1__."first_name")) as "firstName"
  graphile-build-pg:sql       from "app"."clients" as __local_1__
  graphile-build-pg:sql       
  graphile-build-pg:sql       where (((__local_1__."sex" IN ($1))) and ((((__local_1__."sex" IS NULL))))) and (TRUE) and (TRUE)
  graphile-build-pg:sql       order by __local_1__."id" ASC
  graphile-build-pg:sql       
  graphile-build-pg:sql       
  graphile-build-pg:sql     ), __local_2__ as (select json_agg(to_json(__local_0__)) as data from __local_0__) select coalesce((select __local_2__.data from __local_2__), '[]'::json) as "data"  +9ms

filter does not work on "computed" columns

Let's assume I have a model with UserGroup <- User <- Task. Then postgraphile automatically provides a tasks connection on a user, but there is no way to query all tasks of a user group. So I add the tasks column to the UserGroup table as computed column, and the generated schema correctly lists this column as TasksConnection.

The generated schema also includes the filter option for this connection, however, when trying to use it on any column of the task table, this results in the error:

Unable to resolve where comparison for filter field '...'

The filter option works fine on the auto-generated TasksConnection on the User table.

Adding "in" or "notIn" but nothing else to connectionFilterAllowedOperators throws

For some reason adding in or notIn to the connectionFilterAllowedOperators throws with GraphQLError: Syntax Error: Expected Name, found }if no other operators are added

Repro:

// broken
const PostGraphileConnectionFilterPlugin = require("postgraphile-plugin-connection-filter");

const postgraphOptions = {
  ...
  appendPlugins: [PostGraphileConnectionFilterPlugin],
  graphileBuildOptions: {
    connectionFilterAllowedOperators: [
      "in",
      "notIn",
    ],
  },
};


// works
const PostGraphileConnectionFilterPlugin = require("postgraphile-plugin-connection-filter");

const postgraphOptions = {
  ...
  appendPlugins: [PostGraphileConnectionFilterPlugin],
  graphileBuildOptions: {
    connectionFilterAllowedOperators: [
      "equalTo",
      "in",
      "notIn",
    ],
  },
};

[email protected] with postgraphile@^4

Filter by values in array

Looking to filter a list of nodes by an array of values and matching that array against an array on the nodes. So for example given the object {colors: ["#efebe9", "#d7ccc8"]} I'm looking to match all nodes which contain the values #efebe9 and #d7ccc8 in their colors arrays.

Right now I would have to:

filter: {
  or: [
    {
      colors: {
        anyEqualTo: "#efebe9"
      }
    },
    {
      colors: {
        anyEqualTo: "#d7ccc8"
      }
    }
  ]
}

But that's both inefficient and quite difficult to programmatically generate when using gql, or am I missing something?

how to use cli on Windows?

I apologize for asking this here as it obviously is a Windows issue.

But it appeared when I tried to run

postgraphile --append-plugins `pwd`/node_modules/postgraphile-plugin-connection-filter/index.js -c postgres:pwd//postgres:pwd@localhost:5432/ae -s ae --cors --watch

on my windows 10 pc. Above command works as expected on my macOS notebook, by the way.

And I have spent a lot of time searching for how to use relative paths in the console on windows and trying out uncountable variants. But to no avail.

I use ConEmu but I tried all variants in the command prompt and PowerShell too.

Most variants simply return:

Error: Cannot find module '/node_modules/postgraphile-plugin-connection-filter/index.js'

This is the most obvious (to me) variant I tried:

postgraphile --append-plugins \node_modules\postgraphile-plugin-connection-filter\index.js -c postgres://postgres:pwd@localhost:5432/ae -s ae --cors --watch

Does any one know how to do this on Windows?

Document handling of `null` literals

I'd like to add something like the following to README.md. Feedback on the language (or the design decision itself) would be much appreciated.

tl;dr This plugin ignores all null literals and empty objects passed via GraphQL queries. Queries such as filter: { field: null } and filter: { field: { equalTo: null } } will not filter the dataset.

GraphQL draws a distinction between including a field with a null literal value and omitting the field (see Section 2.9.5 of the spec). Servers must be prepared to handle either type of input, and can decide whether to process them differently.

In the context of a traditional CRUD update mutation, the semantics of the null literal are well understood: null clears the value in the database.

In the context of this plugin, a case could be made for inputs such as { field: null } to be interpreted as a where clause of field IS NULL. But equating null with SQL NULL quickly breaks down. Should filter: { field: { equalTo: null } } resolve to field = NULL or field IS NULL, or should it be ignored?

It gets more complicated when filtering across relations. Should { account: null } Where there is a related table named account, should { account: null } check that no related rows exist in the account table, or should it be ignored?

Rather than relying on documenting these clever uses of null, this plugin keeps it simple: null-valued fields are ignored. (Use the isNull operator for null value checks.)

The same goes for passing empty objects to input object fields: they are ignored.

See the tests for examples.

As a nice side effect of this approach, you never have to worry about null vs. undefined (or {} vs. null vs. undefined for input object fields) when building your GraphQL query variables in client-side code.

What is the corret way to use OR

I am having an hard time getting OR to work. It is applying AND when I check the query executed SQL query.

query { allPosts( first: 10 filter: { or:{headline:{ilike: "%Mayer%"}} body:{ilike: "%Mayer%"}} ) { edges { node { id headline body } } } }

filter: { or:{headline:{ilike: "%Mayer%"}} body:{ilike: "%Mayer%"} }
filter: { headline:{ilike: "%Mayer%"} or:{ body:{ilike: "%Mayer%"}} }
filter: { or:{ headline:{ilike: "%Mayer%"} body:{ilike: "%Mayer%"}} }

This is what I am trying to accomplish.
select post.* from forum_example.post as post where post.headline ilike ('%' || search || '%') or post.body ilike ('%' || search || '%')

Add `graphile-build-pg` as an explicit dependency

It's used here:

const { omit } = require("graphile-build-pg");

The omit method doesn't need to come from the same version of graphile-build-pg as the schema is built with, so it doesn't matter if this causes a duplicate (although hopefully it won't!)

Reason this came to my attention:

graphile/crystal#778

In future, I should add it to the build object: graphile/graphile-engine#235

Add logical PostGIS operators support to the plugin

They include the official logical operators (see https://postgis.net/docs/reference.html, section 8.8) and other operators returning a boolean value such as spatial relationships functions (e.g. ST_Disjoint, see section 8.9).

An experimental extension was made in the fork https://github.com/jepetko/postgraphile-plugin-connection-filter/commits/master incl. test setup of the dedicated "postgis" schema alongside the already existing "p" schema.

Example (see https://github.com/jepetko/postgraphile-plugin-connection-filter/blob/master/__tests__/fixtures/postgis-queries/postgis-connections-filter.graphql):

query {
  overlapping: allFilterables(filter: { theGeom: { overlapsBB: "LINESTRING(1 2, 4 6)" } }) { ...filterableConnection }
  disjointG: allFilterables(filter: { theGeom: { disjointsG: "LINESTRING(0 5, 4 6)" } }) { ...filterableConnection }
  intersectsG: allFilterables(filter: { theGeom: { intersectsG: "LINESTRING(-1 1, 3 1)" } }) { ...filterableConnection }
}

fragment filterableConnection on FilterablesConnection {
  pageInfo {
    ...
  }
  totalCount
  edges {
    cursor
    node {
      id
      theGeom
    }
  }
}

how to append this plugin when using pm2

How to append plugins is explained in two places:

Both do not explain how this would work if you are using something like pm2 to start the server which I guess almost everyone does in production.

I have tried a few solutions:

  1. using ecosystem.config.js:
module.exports = {
  apps: [{
    name: 'postgraphile',
    script: './node_modules/.bin/postgraphile',
    args: '-c postgres://user:password@localhost:5432/dbname --jwt-token-identifier auth.jwt_token --default-role postgres --jwt-secret secret -s dbname --cors --host 0.0.0.0 --append-plugins `pwd`/node_modules/postgraphile-plugin-connection-filter/index.js --disable-query-log',
    instances: 1,
    exec_mode: 'cluster',
  }],
}

then starting using it:

pm2 start ecosystem.config.js
  1. passing options inline:
pm2 start ./node_modules/.bin/postgraphile -- -c postgres://user:password@localhost:5432/dbname --append-plugins `pwd`/node_modules/postgraphile-plugin-connection-filter/index.js --jwt-token-identifier auth.jwt_token --default-role postgres --jwt-secret secret -s dbname --cors --disable-query-log

Both lead to this error:

1|postgrap | Failed to load plugin './node_modules/postgraphile-plugin-connection-filter/index.js'
1|postgrap | Error: Cannot find module './node_modules/postgraphile-plugin-connection-filter/index.js'
1|postgrap |     at Function.Module._resolveFilename (module.js:547:15)
1|postgrap |     at Function.Module._load (module.js:474:25)
1|postgrap |     at Module.require (module.js:596:17)
1|postgrap |     at require (internal/module.js:11:18)
1|postgrap |     at names.map.rawName (/home/alex/node_modules/postgraphile/build/postgraphile/cli.js:198:20)
1|postgrap |     at Array.map (<anonymous>)
1|postgrap |     at loadPlugins (/home/alex/node_modules/postgraphile/build/postgraphile/cli.js:190:18)
1|postgrap |     at Object.<anonymous> (/home/alex/node_modules/postgraphile/build/postgraphile/cli.js:262:20)
1|postgrap |     at Module._compile (module.js:652:30)
1|postgrap |     at Object.Module._extensions..js (module.js:663:10)

But it works if I use:

npx postgraphile \
  -c postgres://user:password@localhost:5432/dbname \
  --append-plugins `pwd`/node_modules/postgraphile-plugin-connection-filter/index.js  \
  --jwt-token-identifier auth.jwt_token \
  --default-role anon \
  --jwt-secret secret \
  -s dbname \
  --cors

So obviously ./node_modules/postgraphile-plugin-connection-filter/index.js exists.

The ecosystem.config approach works for me in a different project where I do not use this plugin.

So what could be wrong?

Filter doesn't pick up computed columns

In my Graphiql browser I can't seem to perform a filter on a computed column

Below are two queries, one works as normal with a filter on a real column, but the other is using a computed column and doesn't work (computed columns are generated as per https://www.graphile.org/postgraphile/computed-columns/, which creates a graphql field based on a postgres function)

Using real column firstName:
screen shot 2018-02-23 at 14 35 15

Using computed column fullName:
screen shot 2018-02-23 at 14 33 15

As you can see in both: fullName is used as a field in the fragment and works, just not in a filter.

I can't seem to get past this, but I would really like to use this library for filtering. I need to concat first and last name to perform a full name search, but I don't know how to do that with this lib, and using a computed column doesn't work either as shown above.

Any help would be massively appreciated.

Many Thanks!

How to filter by a key inside an object?

Looking to filter by a key inside a nested JSON object. How would I accomplish that?

query

filter: {
  details: {
    selling_asset_code: "USD"
  }
}

data

{
  "data": {
    "allHistoryOperations": {
      "nodes": [
        {
          "transactionId": "33362562925989890",
          "details": {
            "price": "2.9973324",
            "amount": "149.1600010",
            "price_r": {
              "d": 100000000,
              "n": 299733237
            },
            "offer_id": 120736,
            "buying_asset_type": "native",
            "selling_asset_code": "USD",
            "selling_asset_type": "credit_alphanum4",
            "selling_asset_issuer": "GA7RXKBZOUA3FCUUS65JRLU5SZD3XBQUGRWL7NVQWU5QOXQW2LUZNBFZ"
          },
          "type": 3
        }
      ]
    }
  }
}

Limit usage to certain roles

Hi,

I'm using PostGraphile along with this library when querying my services from other services, which allows me to use GraphQL when my services talk to each other (instead of direct queries to their respective databases). I never want to expose this filter to any end users. Since I have roles set up for service-to-service access, it would be great if I could whitelist them. I'm not sure it would be possible, but it would be ideal if we could hook into the same JWT system that PostGraphile uses.

If this is already possible, it would be much appreciated if I could be pointed into the right direction.

Support "Between"

"Between" is a bit different from the other filters, since it will require two parameters be passed instead of just one. My first thought was that it would be simple to the programmer to provide a filter syntax like:

{
  records(filter: {field: {between: {start: "foo", end: "bar"}}}) {
   // stuff  ...
  }
}

However I'm not sure if this is easy to implement in the current architecture, and before I spent too much time investigating, I'd like to hear if you have an opinion on how to implement multi-parameter operators like this.

edit: fixed example

Filter By Foreign Key

Hi,

Firstly thank you for the brilliant plugin. I am trying to work out how to filter by a field in foreign key. Can you think of a way that this might be possible? I could run a filter first on the foreign key field and get all the ids then do a second in query but I would like to know if there is a better way.

Beta version

Hi Graphile contributors.

First at all, congratulations for this nice library.

We are starting to use it and looks very good!

I saw that the library version is in beta so I have a question related to it:

  • Do you recommend to use it in production? Is the latest version enough stable?

Thanks in advance!

"in" filter with empty array throws syntax error

Currently, it doesn't seem to be possible to have a filter condition like the following:

{ status: { in: [] } }

For me, this would be a valid use case, since the user can select from a list of relevant statuses - and when no statuses are selected, returning an empty result would be the desired behaviour (and I think this would happen when translating this to the respective SQL query).

Is there an option like connectionFilterAllowNullInput or connectionFilterAllowEmptyObjectInput for arrays as well? Or is this actually a bug and not the intended behaviour?

is possible to have @include in filters

is possible to have @include in filters
example:
query searchPabl($search: String,
$judet: String, $lot: [Int!] = [1,2,3,4,5,6,7]) {
allRonetSirutas(filter: { or:[
{pabl:
{containsInsensitive: $search}
},
{pi:
{containsInsensitive: $search}
},
]
and:[{judet:
{containsInsensitive: $judet}
},
{lot:
{in: $lot}
} @include(lot: $lot) ]
})

Filtering on certain functions returning tables does not work

Filtering does not work if the function is defined this way:

CREATE FUNCTION f_problem() RETURNS TABLE (
  a INTEGER,
  b INTEGER
) AS $$
  SELECT 1, 2;
$$ LANGUAGE sql STABLE STRICT;

Then this graphql clause fails: filter: {a: {equalTo: 0}}

The workaround is to define the function with a named output table:

CREATE TABLE t_unused (
  a INTEGER,
  b INTEGER
);

CREATE FUNCTION f_ok() RETURNS SETOF t_unused AS $$
  SELECT 1, 2;
$$ LANGUAGE sql STABLE STRICT;

Affected version: 1.0.0-beta.27

Brief analysis showed that sourceTypeName const was String.
https://github.com/graphile-contrib/postgraphile-plugin-connection-filter/blob/v1.0.0-beta.27/src/PgConnectionArgFilterPlugin.js#L46

Support individual fields multiple times

This one is probably going to be a PITA, but here it is.

The reason behind #7 is that, at present, the same field cannot be named more than once. Filters that result in queries with where clauses like WHERE foo > 1 AND foo < 10 seem to be impossible, since there isn't a way to name the foo field twice. Initially I thought this might be a bigger-picture issue to raise with Benji in postgraphql, but I can't actually think of anywhere outside the filter that this is an issue.

Do you have any thoughts on how to approach this? Perhaps making the filter for each field an array of filter statements instead of a single object, like this?

{
  records(filter: {foo: [{gte: 1}, {lte:10}]}) {
    // ...
  }
}

Or something along those lines? Of course it would be nice if there were also some way to specify if the condition should be an AND or an OR.

Edit: Ignore this, I was being a fool.

Computed columns not works

I try to create computed column for filter:

  create function app.clients_category_ids(c app.clients)
  returns bigint[]
  language sql stable
  as $$
    select array_agg(client_category_id) from app.client_client_categories
    where client_id = c.id;
  $$;

But its rejected here:

const extendFilterFields = (memo, fieldName, fieldType, fieldWithHooks) => {
if (
!(
fieldType instanceof GraphQLScalarType ||
fieldType instanceof GraphQLEnumType
) ||
!fieldType.name
) {
return memo;
}

Because fieldType == [BigInt] without name.

what am I doing wrong?

Null check syntax: `null` or `is`

In the alpha releases, null checks were performed by:
body: { null: true } or body: { null: false }

In the first beta release, I added an is operator to allow:
body: { is: NULL } or body: { is: NOT_NULL }

I only want to support one or the other in v1.0.0. My preference is the is operator, as I think it's easier to read inside complex queries. Anyone else have a preference?

filter plugin

When I try to append-plugins postgraphile-filter-plugins I get the following error:

_Error: Type 'PgIndexFilter' has already been registered!
at Object.newWithHooks (/usr/local/lib/node_modules/postgraphile/node_modules/graphile-build/node8plus/makeNewBuild.js:418:17)
at introspectionResultsByKind.class.map.table /postgraphile-filter-plugins-master/src/PgConnectionArgFilterPlugin.js:57:9)
at Array.map ()
at PgConnectionArgFilterPlugin.builder.hook (/postgraphile-filter-plugins-master/src/PgConnectionArgFilterPlugin.js:52:40)
at SchemaBuilder.applyHooks (/usr/local/lib/node_modules/postgraphile/node_modules/graphile-build/node8plus/SchemaBuilder.js:149:20)
at SchemaBuilder.createBuild (/usr/local/lib/node_modules/postgraphile/node_modules/graphile-build/node8plus/SchemaBuilder.js:183:10)
at SchemaBuilder.buildSchema (/usr/local/lib/node_modules/postgraphile/node_modules/graphile-build/node8plus/SchemaBuilder.js:189:26)
at Object.exports.createPostGraphQLSchema (/usr/local/lib/node_modules/postgraphile/node_modules/postgraphile-core/node8plus/index.js:80:18)
at
at process.tickCallback (internal/process/next_tick.js:188:7)

Custom operator names

I'd like to replace the current connectionFilterUsesShortNames boolean option with a way to specify custom names. Beyond the basic Mongo-style operators (ne, eq, gt, gte, etc.), we haven't been following any specific standard for the short names, and I'd rather not promulgate yet-another-standard apart from the long names. The new option would work like this:

postgraphile(pgConfig, schema, {
  ...
  graphileBuildOptions: {
    connectionFilterOperatorNames: {
      equalTo: "eq",
      notEqualTo: "ne",
      ...
    }
  },
})

If anyone is against this change, please let me know.

Unable to resolve filter on JSON computed columns from function using `returns setof`

I'm currently having an issue filtering on a computed column, that is return through a function using returns setof. Not sure if this is a bug or just my terrible DB structure.

We have recurring events, and people attached to those events. I created a function which generates the "dummy" rows (duplicate data with dates changed etc), which returns a modified set of rows from the event table.
The events have a computed column to return the IDs of the people attending in JSON. This is temporary while issue #26 is pending, allowing us to filter easily.

With any filter added to the computed column, I receive an error.

Query:

{
  exampleEventCalendar(filter: {peopleIds: {isNull: false}}) {
    edges {
      node {
        id
      }
    }
  }
}

Response:

{
  "errors": [
    {
      "message": "Unable to resolve where comparison for filter field 'peopleIds'",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "exampleEventCalendar"
      ]
    }
  ],
  "data": null
}

Filtering peopleIds on allExampleEvents works correctly.


Tables and data

CREATE SCHEMA IF NOT EXISTS "private"
CREATE EXTENSION IF NOT EXISTS "uuid-ossp" schema "private";

DROP TABLE IF EXISTS "example_event";
CREATE TABLE "public"."example_event" (
    "id" uuid DEFAULT private.uuid_generate_v4() NOT NULL,
    "name" text NOT NULL,
    CONSTRAINT "example_event_id" PRIMARY KEY ("id")
) WITH (oids = false);

INSERT INTO "example_event" ("id", "name") VALUES
('cb3268d5-e864-4fad-9d2b-3a73412dccb0',	'test');

DROP TABLE IF EXISTS "example_event_person";
CREATE TABLE "public"."example_event_person" (
    "example_event_id" uuid NOT NULL,
    "example_person_id" uuid NOT NULL,
    CONSTRAINT "example_event_person_example_event_id_example_person_id" PRIMARY KEY ("example_event_id", "example_person_id"),
    CONSTRAINT "example_event_person_example_event_id_fkey" FOREIGN KEY (example_event_id) REFERENCES example_event(id) NOT DEFERRABLE
) WITH (oids = false);

INSERT INTO "example_event_person" ("example_event_id", "example_person_id") VALUES
('cb3268d5-e864-4fad-9d2b-3a73412dccb0',	'1e979564-5709-4a08-9502-e832b8fca9af');

People Ids computed column

create or replace function public.example_event_people_ids(t public.example_event)
returns jsonb as $$
    select to_jsonb(ARRAY(select p.example_person_id::text from public.example_event_person p
        where p.example_event_id = t.id));
$$ language sql stable;

Calendar function

Stripped down version of the calendar function.

create or replace function example_event_calendar()
returns setof public.example_event
language plpgsql stable
as $BODY$
declare
    _row example_event;
begin
    for _row in
        select * from public.example_event
    loop
        return next _row;
    end loop;
    return;
end;
$BODY$

Is this a legitimate issue, or should I refactor how I'm doing things?

Filtering against boolean is not working.

allInvoices(
first: 100 
filter:{isDeleted:{eq:false}}
) {

return

 "message": "syntax error at or near \")\""

Database Log message:

2017-10-25 22:22:10 EDT LOG:  statement: begin
2017-10-25 22:22:10 EDT ERROR:  syntax error at or near ")" at character 219
2017-10-25 22:22:10 EDT STATEMENT:
  with __local_0__ as (
	      select to_json(__local_1__."id") as "id", to_json(__local_1__."is_deleted") as "isDeleted"
	      from "application"."credits" as __local_1__
	      
	      where (((__local_1__."is_deleted" = ))) and (TRUE) and (TRUE)
	      order by __local_1__."id" ASC
	      limit 100
	      
	    ), __local_2__ as (select json_agg(to_json(__local_0__)) as data from __local_0__) select coalesce((select __local_2__.data from __local_2__), '[]'::json) as "data", (
	      select count(*)
	      from "application"."credits" as __local_1__
	      where (((__local_1__."is_deleted" = )))
	    ) as "totalCount"
2017-10-25 22:22:10 EDT LOG:  statement: commit
allInvoices(
first: 100 
filter:{isDeleted:{eq:true}}
) {

return

"message": "Expected SQL item, instead received 'true'."

No Log message return

Filter On Single Relations

I have

CREATE TABLE publisher (
    publisher_id serial PRIMARY KEY,
    name text
);

CREATE TABLE author (
    author_id serial PRIMARY KEY,
    name text
);

CREATE TABLE book (
    book_id serial PRIMARY KEY,
    title text
    publisher integer REFERENCES publisher(publisher_id),
    author integer REFERENCES author(author_id)
);

I display all the books in a table from the query:

query allBooks {
    allBooks {
        nodes {
            book_id
            title
            publisherByPublisher {
                name
            }
            authorByAuthor {
                name
            }
        }
    }
}

I want to be able to apply arbitrary filters such as all the books with publisher.name ilike '%foo%' or author.name ilike '%bar%' or both. Also, I would like to be able to construct a single query that accepts filters but will return all results if "unfiltered."

This seems somewhat related to #26 but I decided to open a new issue because of how graphile has changed since that one was opened. Looking into how to implement this myself but any assistance you can provide would be greatly appreciated.

please add example of how to filter

For someone coming new to this tool it is not clear how to query:

  • What does filter: expect?
  • What are possible filter operators?
  • How about and and or?

Once you've found it it's so simple. So I think a short example would help a lot.

Somehow I could not find that in the tests. Must have missed it. I finally found an example in other issues.

Or maybe this is documented somewhere else? If that is the case a link in the readme would help.

O.k., now I found out how pros do it: they know how to use GraphiQL to find it all. But I only found that out after I knew how the basic syntax works and I could write a working example in GraphiQL.

So I guess a simple example accompanied by a pointer to GraphiQL would be great.

plugin works as expected but produces error output

First of all let me thank you for the great work on this plugin!

I'm using [email protected] with [email protected].

With a query like the following:

query {
  allTasks(filter: { status: { in: [STATUSA, STATUSB]} }) {
    nodes {
      name
      status
    }
  }
}

I do get the expected results, so the plugin seems to be working as intended. However, on every graphql request, error output like the following is produced on the console:

0 error(s) as root in 9.97ms :: { allTasks(filter: {status: {in: [STATUSA, STATUSB]}}) { nodes { name status } } }
gql2pg should be called with three arguments, the third being the type modifier (or `null`); Error
at Object.gql2pg (/usr/node_modules/graphile-build-pg/node8plus/plugins/PgTypesPlugin.js:128:17)
at Array.isArray.pgType.isPgArray.sql.query.sql.join.input.map.i (/usr/node_modules/postgraphile-plugin-connection-filter/src/PgConnectionArgFilterPlugin.js:388:39)
at Array.map (<anonymous>)
...

Using a String query parameter as a value in a JSON "contains" test is always true

In a table that has a JSON type data column:
insert into myTable(jsonData) values ({"stringField": "test"}, {"stringField": "notTest"})

if you run the query:

query myQuery { 
  myTable (
    filter: { jsonData: {contains: { stringField: "test" } } }
  ), { totalCount } }

You get the expected answer of 1 for totalCount.

However if you use a query parameter to supply the value; like this:

query myQuery($myVal: String) { 
  myTable (
    filter: { 
      jsonData: {contains: { stringField: $myVal } } 
    }
  ), {
    totalCount 
  } 
}
Query Parameters:
{"myVal": "test"}

I'm getting the unexpected answer of 2.

Getting PgIndexFilter Error

Hey mate, so looks like this was solved before but has managed creep back in:

Error: Type 'PgIndexFilter' has already been registered!
at Object.newWithHooks (/home/ubuntu/server/backend_lnk/node_modules/graphile-build/src/makeNewBuild.js:603:17)
at /home/ubuntu/server/backend_lnk/plugins/postgraphile-filter-plugin/src/PgConnectionArgFilterPlugin.js:57:9
at Array.map (native)
at /home/ubuntu/server/backend_lnk/plugins/postgraphile-filter-plugin/src/PgConnectionArgFilterPlugin.js:52:40
at SchemaBuilder.applyHooks (/home/ubuntu/server/backend_lnk/node_modules/graphile-build/src/SchemaBuilder.js:289:20)

Would love any feedback

filter on specific date without caring about min/sec

what is a good way to filter on specific date?
I am storing data as full date format with timezone like 2018-08-06T14:21:54+02:00 and I am trying to query for specific day like: 2018-08-06
There are only few functionalities available for Date field and none support something like includes.
Any suggestion?

ps: I know one solution would be making a function/store procedure in Postgres and then call it in the graphql but that is quite limited.

How can I use "or"?

These are some great plugins! I have a field that I'm trying to accept 2 different values for (null or greaterThan some date).

Here are the variables I'm currently trying:

{
  "eventsFilter": {
    "groupId": { "equalTo": 14 },
    "and": {
      "when": { "null": true },
      "or": {
        "when": { 
          "greaterThan": "2017-11-07" 
        }
      }
    }
  }
}

I've also tried

{
  "eventsFilter": {
    "groupId": { "equalTo": 14 },
    "or": {
      "when": {
        "null": true,
        "greaterThan": "2017-11-07" 
      }
    }
  }
}

If there is no way to do this currently, that's fine, I have a workaround :).

totalCount and nodes.length do not agree

I am using a very complicated query to build a complex tree. Part of this tree is filtering dependent on dynamic variables. So I have begun to feed in a dynamic filter $apFilter: https://github.com/barbalex/apf2/blob/feb96a0cabc35426763f88649bfd42e2b466e7d8/src/components/Projekte/ProjektContainer/data2.graphql#L37-L55

$apFilter might for instance look like this: {projId: {in: "e57f56f4-4376-11e8-ab21-4314b6749d13"}}. When this query renders, data for the aps key looks like:
1

Then when the user flips a switch it is changed to: {projId: {in: "e57f56f4-4376-11e8-ab21-4314b6749d13"}, bearbeitung: {in: [1, 2, 3]}} and the data looks like this:
2
Note the fact that totalCount is 58 while nodes.length is 573! This is not only after the data passed through apollo-react but in the raw data passed back from postgraphile.

Only after the user interacts with the tree again and it renders once more does the data look as expected:
3

Am I not supposed to do this this way?
Or am I doing something wrong?

Multiple functions returning same setof custom type throws error

Schema:

This is a simple example to reproduce the error.

create extension "uuid-ossp";

create type public.stat as (
    id uuid
);

create table public.stat_table (
    id uuid primary key default uuid_generate_v4()
);

create function stat_report() returns setof public.stat as $$
    select * from stat_table;
$$ language sql stable;

create function stat_report2() returns setof public.stat as $$
    select * from stat_table;
$$ language sql stable;

insert into public.stat_table (id) values
      (default)
    , (default)
    , (default)
    , (default)
    , (default)
    , (default)
    , (default)
    , (default)
    , (default)
    , (default)
    , (default);

Throws:

{"errors":[{"message":"Input Object type StatFilter must define one or more fields."}]}

If you drop, or add '@omit' comment to, one of the report functions it works.

I think I can get around this issue by refactoring using temp tables and/or "template" tables.

Cheers!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.