Apollo/GraphQL: как получить вложенные элементы?

Это пример документа моей mongoDB. Мне нужно получить массив content.en через Apollo/GraphQL. Но у меня проблема с вложенным объектом. en — это языковой тег, поэтому было бы здорово, если бы его можно было использовать как переменную.

Данные в MongoDB

{
    "_id" : "9uPjYoYu58WM5Tbtf",
    "content" : {
        "en" : [
            {
                "content" : "Third paragraph",
                "timestamp" : 1484939404
            }
        ]
    },
    "main" : "Dn59y87PGhkJXpaiZ"
}

Результат graphQL должен быть:

{
  "data": {
    "article": [
      {
        "_id": "9uPjYoYu58WM5Tbtf",
        "content": [
                {
                    "content" : "Third paragraph",
                    "timestamp" : 1484939404
                }
            ]
      }
    ]
  }
}

Это означает, что мне нужно получить идентификатор и массив содержимого для конкретного языка.


Но это не то, что я получаю со следующей настройкой:

Тип

const ArticleType = new GraphQLObjectType({
  name: 'article',
  fields: {
    _id: { type: GraphQLID },
    content: { type: GraphQLString }
  }
})

Схема GraphQL

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)
          }
        },
        async resolve ({ db }, args) {
          return db.collection('articles').find({ main: args.id }).toArray()
        }
      }
    }
  })
})

Запрос

{
  article(id: "Dn59y87PGhkJXpaiZ") {
    _id,
    content
  }
}

Результат

{
  "data": {
    "article": [
      {
        "_id": "9uPjYoYu58WM5Tbtf",
        "content": "[object Object]"
      }
    ]
  }
}

person user3142695    schedule 25.10.2017    source источник
comment
Что именно вы ожидаете, что запрос вернет? Не могли бы вы добавить это к своему вопросу?   -  person DevNebulae    schedule 28.10.2017
comment
@DevNebulae Я уже публиковал это. См. второй блок кода. Результат graphQL должен быть   -  person user3142695    schedule 28.10.2017
comment
Я считаю, что вам нужны два аргумента: аргумент id для фильтрации статей по корневому разрешению и языковые аргументы для фильтрации контента по дополнительному разрешению. Таким образом, запрос mongo вернет все соответствующие статьи для id со всем языковым содержимым, а graphql вернет содержимое на основе языкового аргумента. Также сопоставьте содержимое со схемой.   -  person s7vr    schedule 30.10.2017


Ответы (3)


Вы можете использовать приведенный ниже код.

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

const ContentType = new GraphQLObjectType({
  name: 'content',
  fields: {
    content: { type: GraphQLString },
    timestamp: { type: GraphQLInt }
  }
})

const ArticleType = new GraphQLObjectType({
  name: 'article',
  fields: {
    _id: { type: GraphQLID },
    content: { 
      type: new GraphQLList(ContentType),
      args: {
          language: {
            name: 'language',
            type: new GraphQLNonNull(GraphQLString)
          }
        },
        async resolve (args) {
          return filter content here by lang 
        }
      }
    }
  }
})

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)
          }
        },
        async resolve ({ db }, args) {
          return db.collection('articles').find({ main: args.id}).toArray()
        }
      }
    }   
  })
})

Пример Java:

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;

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


public class GraphQLTest {

    private static final ArticleRepository articleRepository;

    public static class ArticleRepository {

        private final 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(new ArrayList<>());
            return allArticles;
        }

    }

    public static void main(String... args) {

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

        ExecutionResult result = GraphQL.newGraphQL(buildSchema()).build().execute(query);

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


    static {
        MongoDatabase mongo = new MongoClient().getDatabase("test");
        articleRepository = new ArticleRepository(mongo.getCollection("articles"));
    }

    private static GraphQLSchema buildSchema() {

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

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

        GraphQLFieldDefinition.Builder articleDefinition = newFieldDefinition()
                .name("article")
                .type(new GraphQLList(ArticleType))
                .argument(newArgument().name("id").type(new GraphQLNonNull(GraphQLID)).build())
                .dataFetcher(dataFetchingEnvironment -> articleRepository.getAllArticles(dataFetchingEnvironment.getArgument("id")));

        return newSchema().query(
                newObject()
                        .name("RootQueryType")
                        .field(articleDefinition)
                        .build()
        ).build();
    }
}
person s7vr    schedule 29.10.2017
comment
Есть ли у вас также намек на это? stackoverflow .com/questions/46929327/ - person user3142695; 30.10.2017
comment
Этот вопрос кажется очень похожим на этот. Вы изменили структуру на новую? Есть ли сейчас новый контент коллекции? Я считаю, что ответ, представленный здесь, будет работать в некоторой степени. Каковы различия? - person s7vr; 30.10.2017
comment
Я лишь немного изменил его для кода примера. Я действительно застрял на этом гнезде. Сначала я думал, что это должно быть довольно просто, так как вы правы, что это очень похожая проблема... - person user3142695; 30.10.2017

Поскольку вы сказали, что ISO-код языка должен быть параметром и что содержимое зависит от ISO-кода языка (с этого момента я буду называть его languageTag), я решил, что вам следует отредактировать свою схему. выглядеть примерно так:

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)
          }
        },

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

Однако это по-прежнему не устраняет проблему с получением содержимого. Я считаю, что вам нужно добавить в вашу схему еще один тип с именем ContentType.

const ContentType = new GraphQLObjectType({
  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
    }
  },
})

Последний вопрос, который я хотел бы затронуть, заключается в том, что вы возвращаете один article как Array. Я бы предложил изменить это, чтобы вернуть один объект. И последнее, но не менее важное: ваша схема будет выглядеть примерно так:

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

Этот код может быть немного неверным, так как у меня нет вашей базы данных для его проверки. Я думаю, что это хороший толчок в правильном направлении.

person DevNebulae    schedule 28.10.2017
comment
Поле языка представляет собой массив, так как существует несколько объектов контента. В моем примере есть только один объект. Но мне нужно пойти на массив. - person user3142695; 28.10.2017
comment
@user3142695 user3142695 Не могли бы вы дать схему вашей проблемы? Я думаю, что мы упускаем большую часть головоломки. - person DevNebulae; 28.10.2017
comment
Кажется, я понимаю, что вы пытаетесь сделать, но все же я получаю результат "content": "[object Object]" - person user3142695; 28.10.2017
comment
Запускается ли поле разрешения в ContentType? т.е. Вы поставили console.log туда и посмотрите, дойдет ли он? - person DevNebulae; 28.10.2017
comment
Я попробовал именно это прямо в данный момент. Нет, разрешение в ContentType не запускает console.log - person user3142695; 28.10.2017
comment
@user3142695 user3142695 Я только что обновил ContentType, что теперь? - person DevNebulae; 28.10.2017
comment
Давайте продолжим обсуждение в чате. - person user3142695; 28.10.2017
comment
Я должен переместить дополнительное поле содержимого в ArticleType и добавить туда языковой аргумент и разрешение. С этим работает. Спасибо за вашу помощь и вашу подсказку. - person user3142695; 30.10.2017

Я думаю, что проблема исходит из этой строки: content: { type: GraphQLString }. Попробуйте извлечь содержимое в другое GraphQLObjectType, а затем передать его в ArticleType как поле content.

Изменить

Попробуй это:

const ContentType = new GraphQLObjectType({
  name: 'content',
  fields: {
    en: { type: GraphQLList },
    it: { type: GraphQLList }
    // ... other languages 
  }
})
person Alexander Vitanov    schedule 28.10.2017
comment
Для меня есть две проблемы с этим? Поле en является переменным. Также может быть de или it. Поэтому мне нужно использовать переменную, чтобы получить правильный языковой объект. Я застрял на этом. А во-вторых, это массив, так что GraphQLString здесь не сработает, верно? Не могли бы вы опубликовать код для лучшего понимания? - person user3142695; 28.10.2017