Comments (15)
@benjie your suggested solution using graphql-weaver works its magic ;)
Had to add this to get the link to work correctly:
helpers:
const base64 = str => new Buffer(String(str)).toString("base64");
const toNodeId = (...args) => base64(JSON.stringify(args));
and in the returned objects of the resolver:
return substanceId !== undefined ? {
..._source,
substanceBySubstanceId: toNodeId("substances", substanceId),
} : {
..._source,
recipeByRecipeId: toNodeId("recipes", recipeId)
};
and in the weaver conf:
{
namespace: "es",
typePrefix: "Es",
schema: esSchema,
fieldMetadata: {
"SearchResult.substanceBySubstanceId": {
// Field substance in type search
link: {
field: "k2.substance", // field substance in namespace k2
argument: "nodeId", // argument of k2.substance
}
},
"SearchResult.recipeByRecipeId": {
// Field recipe in type search
link: {
field: "k2.recipe", // field recipe in namespace k2
argument: "nodeId", // argument of k2.recipe
}
}
}
}
from graphile-engine.
@benjie Thanks! I'm actually trying to make them available side-by-side, to be able to compare the relevance ranking, the common part is that they return results of the same data type / shape { substanceId, recipeId } (both bigint).
But I see now that stitching makes more sense because of the current single root query constraint in postgraphile, its great for getting efficient pg queries. Stitching should achieve all our current needs, efficiently enough anyway.
from graphile-engine.
Thank you! @benjie and @calebmer Without your wonderful contributions this wouldn't be nearly as great! :)
Couldn't thank you guys enough!
from graphile-engine.
Not sure why you'd use
gettTypeByName(pgInflection.inputType("page"))
and not simply:
getTypeByName("PageInput")
I've modified existing types (i.e. payload not input, but similar) using somewhat different logic:
export default function EmailVerificationPlugin (builder) {
builder.hook('GraphQLObjectType:fields',
(
fields,
{ extend, getTypeByName },
context
) => {
const { scope: { isMutationPayload }, GraphQLObjectType} = context
if (!isMutationPayload || GraphQLObjectType.name !== 'AuthenticatePayload') {
return fields // we only are interested in the AuthenticatePayload mutation
}
const jwtResolver = fields.jwt.resolve;
return { ...fields,
jwt: { ...fields.jwt,
resolve: (...resolveParams) => {
// Note that at this point SQL function authenticate() is already executed.
sendConfirmationEmailIfNeeded(resolveParams)
return jwtResolver(...resolveParams)
}
}
}
}
)
}
from graphile-engine.
maybe the example did not have enough context, here is what I’m trying to do:
const postgraphile = require("./postgraphile");
const sql = require ("pg-sql2");
module.exports = function BeeSquadMutations(builder, {pgInflection}) {
builder.hook("GraphQLObjectType:fields", (
fields,
{
extend,
findGqlTableType,
gql2pg,
graphql: {GraphQLNonNull, GraphQLID, GraphQLString, GraphQLList},
},
{scope: {isRootMutation}, fieldWithHooks}
) => {
if (!isRootMutation) {
return fields;
}
return extend(fields, {
CreateDiscussion: fieldWithHooks("CreateDiscussion", postgraphile.fieldSpec({
spec: {
type: new GraphQLNonNull(findGqlTableType("discussion")),
args: {
content: { type: new GraphQLNonNull(GraphQLString) },
page: {
type: new GraphQLNonNull(getTypeByName(pgInflection.inputType("page"))),
}
},
},
table: sql.identifier("graphql", "discussion"),
resolvedData: async ({content, page: {url, canonical_url = null, keywords = []}}, {session, streams}) => {
// [snip]
},
}, gql2pg)),
});
});
};
I could use getTypeByName("PageInput")
, it’s exactly the same as getTypeByName(pgInflection.inputType("page"))
, getTypeByName
returns undefined
.
so I’m not updating an existing type, I’m building a new one, and in it I’d like to reuse an existing one
in other places I’ve done some filtering on introspectionResultsByKind.class
to find the type.id
to call pgGetGqlTypeByTypeId
but I don’t if I can find one for an input type
from graphile-engine.
I suspect the issue is that the type has not been defined at the time that you request it because they're now lazily evaluated. Though it could be just that I forgot to add the addType
call somewhere and never realised.
Try using pgGetGqlTypeByTypeId
(you can find the type id from the introspection results) for the Page table/view/whatever BEFORE calling getTypeByName.
Or try adding your plugin later so the type definitely exists first.
Or tell me that I forgot the addType
call if that's the case ;)
GL!
from graphile-engine.
To be clear above - getting the type with pgGetGqlTypeByTypeId
of the base type Page
also registers other types at the moment.
Though in this case I think you might be after pgGetGqlInputTypeByTypeId
from graphile-engine.
it seems odd that getTypeByName
would return undefined
and that trying to register the PageInput
type throws :
Error: Schema must contain unique named types but contains multiple types named "PageInput".
the plugin is actually added after all other default plugins
I’m going to try it with pgGetGqlInputTypeByTypeId
and also see if getTypeByName
works once I got it with pgGetGqlInputTypeByTypeId
from graphile-engine.
so I managed to get the input type with pgGetGqlInputTypeByTypeId
, thx @benjie
and after that getTypeByName
for this type also worked. so even though the plugin was in last position I cannot use getTypeByName
.
If this is expected you can close this issue
from graphile-engine.
You can use DEBUG=“graphile*” envvar to see the order the various hooks are executing in which should give you a better understanding of what’s going on. Please report back 👍
from graphile-engine.
I'm trying to achieve a bit similar thing. I have a pg stored proc for multi-table (substance and recipe) search, I have defined a SearchResult table structure with foreign keys for the results {substanceId, recipeId} to allow traversing the relation, and have the substanceBySubstanceId and recipeByRecipeId resolvers generated automatically, this works correctly.
I have now set up elasticsearch and made a graphile plugin to return the same structure and reuse the resolvers. But, the substanceBySubstanceId and recipeByRecipeId keep returning null, @benjie you have any insight why? Or should I just dive into the library and resolution logic?
const search = require("./search");
module.exports = function ElasticSearchPlugin(builder) {
builder.hook("GraphQLObjectType:fields", (
fields,
build,
{ scope: { isRootQuery } } // Context object - used for filtering
) => {
if (!isRootQuery) {
// This isn't the object we want to modify:
// return the input object unmodified
return fields;
}
const {
extend,
getTypeByName,
graphql: { GraphQLNonNull, GraphQLString, GraphQLList }
} = build;
const SearchResult = getTypeByName("SearchResult");
return extend(fields, {
esSearch: {
description: `Searches in ElasticSearch`,
type: new GraphQLList(SearchResult),
args: {
query: {
type: new GraphQLNonNull(GraphQLString)
},
index: {
type: GraphQLString
}
},
resolve: search
}
});
});
};
from graphile-engine.
@msand I think that's a completely different thing since you're effectively trying to replace the resolver with one from a different source.
To make it work you need to ensure your data is returned in the right format. I believe the connection should resolve to an object with the following keys:
data
- an array of resultshasNextPage
- booleanhasPreviousPage
- booleantotalCount
- int
where each result has the GraphQL aliases of the fields requested plus __cursor
containing the cursor. (I can't remember what the shape of the cursor is on the PG side.)
However because PostGraphile resolves each root field to just one query (no matter how deep the fields are) your resolver will need to do the same. This is something I intend to improve over time (i.e. make it so that a Postgres field can take over from a non-postgres field) but I haven't spent any time on that right now - you may be better off achieving it by stitching your PostGraphile schema together with an elastic search schema using something like graphql-weaver
.
from graphile-engine.
Thanks for sharing @msand! 👍
from graphile-engine.
[semi-automated message] To keep things manageable I'm going to close this issue as I think it's solved; but if not or you require further help please re-open it.
from graphile-engine.
(This is not the DX I ultimately want, but that's something we're going to have to hone after we've released v4 I think.)
from graphile-engine.
Related Issues (20)
- [graphql-parse-resolve-info] Fix types or example HOT 3
- Custom functions for `nodeId` in `NodePlugin` HOT 2
- Add depth traversal to `simplifyParsedResolveInfoFragmentWithType` HOT 2
- No Way to Access Request From Resolvers? HOT 1
- [graphile-build]: Types for Build Don't Match Source HOT 4
- graphql-parse-resolve-info scalar fields HOT 2
- Pagination cursors not working for tables with at least one bigint component HOT 6
- Modify column SQL select HOT 1
- graphile/subscriptions-lds does not trigger on DELETE if subscription contains only edges
- Project depends on vulnerable version of jsonwebtoken HOT 2
- graphql-parse-resolve-info - What to do to get the same output like graphql-fields? HOT 5
- Don't silently swallow exceptions in resolveNode HOT 2
- alias a column at mutation time only? HOT 2
- Type with array property triggers error HOT 10
- Error using PG client: "We only support PG clients from a PG pool..." HOT 4
- pg-pubsub call to removeListener looks like it will not work HOT 3
- Provide a type guard to distinguish between ResolveTree and FieldsByTypeName HOT 2
- Multiple services resulting in duplicate codecs for simple types HOT 1
- Defining two types with the same schema should be allowed HOT 1
- multi tenancy and add schemas for watch HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from graphile-engine.