Giter Club home page Giter Club logo

Comments (15)

msand avatar msand commented on August 18, 2024 2

@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.

msand avatar msand commented on August 18, 2024 1

@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.

msand avatar msand commented on August 18, 2024 1

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.

chadfurman avatar chadfurman commented on August 18, 2024

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.

mathroc avatar mathroc commented on August 18, 2024

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.

benjie avatar benjie commented on August 18, 2024

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.

benjie avatar benjie commented on August 18, 2024

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.

mathroc avatar mathroc commented on August 18, 2024

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.

mathroc avatar mathroc commented on August 18, 2024

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.

benjie avatar benjie commented on August 18, 2024

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.

msand avatar msand commented on August 18, 2024

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.

benjie avatar benjie commented on August 18, 2024

@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 results
  • hasNextPage - boolean
  • hasPreviousPage - boolean
  • totalCount - 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.

benjie avatar benjie commented on August 18, 2024

Thanks for sharing @msand! 👍

from graphile-engine.

benjie avatar benjie commented on August 18, 2024

[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.

benjie avatar benjie commented on August 18, 2024

(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)

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.