Skip to content Skip to sidebar Skip to footer

Apollo/graphql: How To Get Nested Elements?

This is an example document of my mongoDB. I need to get the content.en array via Apollo/GraphQL. But the nested object is getting a problem for me. en is the language tag, so it w

Solution 1:

You can use below code.

String query = {
  article(id: "Dn59y87PGhkJXpaiZ") {
    _id,
    content(language:"en") {
      content,
      timestamp
    }
  }
}

constContentType = newGraphQLObjectType({
  name: 'content',
  fields: {
    content: { type: GraphQLString },
    timestamp: { type: GraphQLInt }
  }
})

constArticleType = newGraphQLObjectType({
  name: 'article',
  fields: {
    _id: { type: GraphQLID },
    content: { 
      type: newGraphQLList(ContentType),
      args: {
          language: {
            name: 'language',
            type: newGraphQLNonNull(GraphQLString)
          }
        },
        async resolve (args) {
          return filter content here by lang 
        }
      }
    }
  }
})

exportdefaultnewGraphQLSchema({
  query: newGraphQLObjectType({
    name: 'RootQueryType',
    fields: {
      article: {
        type: newGraphQLList(ArticleType),
        description: 'Content of article dataset',
        args: {
          id: {
            name: 'id',
            type: newGraphQLNonNull(GraphQLID)
          }
        },
        async resolve ({ db }, args) {
          return db.collection('articles').find({ main: args.id}).toArray()
        }
      }
    }   
  })
})

Java Example:

import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.*;
import org.bson.Document;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

importstatic graphql.Scalars.*;
importstatic graphql.schema.GraphQLArgument.newArgument;
importstatic graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
importstatic graphql.schema.GraphQLObjectType.newObject;
importstatic graphql.schema.GraphQLSchema.newSchema;


publicclassGraphQLTest {

    privatestaticfinal ArticleRepository articleRepository;

    publicstaticclassArticleRepository {

        privatefinal MongoCollection<Document> articles;

        ArticleRepository(MongoCollection<Document> articles) {
            this.articles = articles;
        }

        public List<Map<String, Object>> getAllArticles(String id) {
            List<Map<String, Object>>  allArticles = articles.find(Filters.eq("main", id)).map(doc -> (Map<String, Object>)doc).into(newArrayList<>());
            return allArticles;
        }

    }

    publicstaticvoidmain(String... args) {

        Stringquery="{\n" +
                "  article(id: \"Dn59y87PGhkJXpaiZ\") {\n" +
                "    _id,\n" +
                "    content(language:\"en\") {\n" +
                "      content,\n" +
                "      timestamp\n" +
                "    }\n" +
                "  }\n" +
                "}";

        ExecutionResultresult= GraphQL.newGraphQL(buildSchema()).build().execute(query);

        System.out.print(result.getData().toString());
    }


    static {
        MongoDatabasemongo=newMongoClient().getDatabase("test");
        articleRepository = newArticleRepository(mongo.getCollection("articles"));
    }

    privatestatic GraphQLSchema buildSchema() {

        GraphQLObjectTypeContentType= newObject().name("content")
                .field(newFieldDefinition().name("content").type(GraphQLString).build())
                .field(newFieldDefinition().name("timestamp").type(GraphQLInt).build()).build();

        GraphQLObjectTypeArticleType= newObject().name("article")
                .field(newFieldDefinition().name("_id").type(GraphQLID).build())
                .field(newFieldDefinition().name("content").type(newGraphQLList(ContentType))
                        .argument(newArgument().name("language").type(GraphQLString).build())
                        .dataFetcher(dataFetchingEnvironment -> {
                            Documentsource= dataFetchingEnvironment.getSource();
                            DocumentcontentMap= (Document) source.get("content");
                            ArrayList<Document> contents = (ArrayList<Document>) contentMap.get(dataFetchingEnvironment.getArgument("lang"));
                            return contents;
                        }).build()).build();

        GraphQLFieldDefinition.BuilderarticleDefinition= newFieldDefinition()
                .name("article")
                .type(newGraphQLList(ArticleType))
                .argument(newArgument().name("id").type(newGraphQLNonNull(GraphQLID)).build())
                .dataFetcher(dataFetchingEnvironment -> articleRepository.getAllArticles(dataFetchingEnvironment.getArgument("id")));

        return newSchema().query(
                newObject()
                        .name("RootQueryType")
                        .field(articleDefinition)
                        .build()
        ).build();
    }
}

Solution 2:

Since you said that the language ISO code should be a parameter and that the content is depending on a language ISO code (I'll call it languageTag from now on), I figured that you should edit your schema to look something like this:

export defaultnew GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'RootQueryType',
    fields: {
      article: {
        type: new GraphQLList(ArticleType),
        description: 'Content of article dataset',
        args: {
          id: {
            name: 'id',
            type: new GraphQLNonNull(GraphQLID)
          },

          // Edited this part to add the language tag
          languageTag: {
            name: 'languageTag',
            type: new GraphQLNonNull(GraphQLString)
          }
        },

        async resolve ({ db }, args) {
          return db.collection('articles').find({ main: args.id }).toArray()
        }
      }
    }   
  })
})

However, this still does not fix your issue of retrieving the content. I reckon that you need to add another type to your schema called ContentType.

constContentType=newGraphQLObjectType({name:'ContentType',fields: {
    content: {
      type:GraphQLString,
      resolve:(root, args, context)=>root.content[args.languageTag].content
    },
    timestamp: {
      type:GraphQLString,
      resolve:(root, args, context)=>root.content[args.languageTag].timestamp
    }
  },})

One final issue I would like to bring up is that you are returning a single article as an Array. I would suggest to change this to return a single object. Last but not least, your schema would look something like this:

export default new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'RootQueryType',
    fields: {
      article: {
        type: new GraphQLList(ArticleType),
        description: 'Content of article dataset',
        args: {
          id: {
            name: 'id',
            type: new GraphQLNonNull(GraphQLID)
          },

          // Edited this part to add the language tag
          languageTag: {
            name: 'languageTag',
            type: new GraphQLNonNull(GraphQLString)
        },

        // Add the extra fields to the article
        fields: {
          content: ContentType
        }

        async resolve ({ db }, args) {
          return db.collection('articles').findOne({ main: args.id })
        }
      }
    }   
  })
})

This code could be a little bit off, since I do not have your database to test it. I think that it is a good push in the right direction.

Solution 3:

I think the problem comes from this line: content: { type: GraphQLString }. Try extracting content to another GraphQLObjectType and then pass it to ArticleType as content field

Edit

Try this:

const ContentType = new GraphQLObjectType({
  name: 'content',
  fields: {
    en: { type: GraphQLList },
    it: { type: GraphQLList }
    // ... other languages 
  }
})

Post a Comment for "Apollo/graphql: How To Get Nested Elements?"