3

I am using graphQL to perform a search across multiple mongoDB collections and API's by combining queries. All queries return a result type of

{
  _id: string; 
  name: string; 
  type: string;
}

Is there any way to flatten the data into a single array?

Combined query example:

query searchAll {
    books(input: {text: "e"}) {
        _id
        name
        type
    }
    magazines(input: {text: "e"}) {
        _id
        name
        type
    }
}

Response currently looks like:

{"data": {
        "books": [
            {
                "_id": "5a8ac759c25b7235ffdc6888",
                "name": "someBook",
                "type": "book"
            }
        ],
        "magazines": [
            {
                "_id": "5a87005bc25b7235ffdc4bdf",
                "name": "someMagazine-1",
                "type": "magazine"
            },
            {
                "_id": "5a870067c25b7235ffdc4be4",
                "name": "someMagazine-2",
                "type": "client"
            }
        ]
    }
}

Desired response:

{"data": {
    "results": [
            {
                "_id": "5a8ac759c25b7235ffdc6888",
                "name": "someBook",
                "type": "book"
            },
            {
                "_id": "5a87005bc25b7235ffdc4bdf",
                "name": "someMagazine-1",
                "type": "magazine"
            },
            {
                "_id": "5a870067c25b7235ffdc4be4",
                "name": "someMagazine-2",
                "type": "client"
            }
        ]
    }
}

1 Answer 1

7

You want to look into using interfaces, here's an example of a (slightly richer) schema definition:

interface Searchable {
  id: ID!
  name: String!
}

type Book implements Searchable {
  id: ID!
  name: String!
  author: Author!
  publisher: Publisher!
  isbn: String!
}

type Magazine implements Searchable {
  id: ID!
  name: String!
  publisher: Publisher!
  issn: String!
}

input SearchInput {
  text: String!
}

type Query {
  search(input: SearchInput!): [Searchable!]!
}

Here's how you'd query it:

query searchAll {
  search(input: {text: "e"}) {
    __typename
    id
    name
    ... on Book {
      author
      isbn
    }
    ... on Magazine {
      issn
    }
  }
}

The resolver for search would be responsible for calling all the different collections and aggregating the results into a single array. Going into more depth than this is implementation-specific, but there should be docs for using interfaces (and unions, which are similar) in whichever GraphQL implementation you're using.

The __typename field is what tells you the concrete type of the returned object, thereby letting front-end code do the appropriate display logic.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the response. I looked into interfaces, and based on your suggestion, I have a few questions: 1. What if book and magazine are always type Searchable, but come from different databases (there is never a need to add additional unique fields)? 2. How does the search resolver look? (compared to the individual Books and Magazines resolvers)
I see. I guess I was thinking about graphQL wrong (just started using it). I thought the point was to NOT create a long API aggregator, but to instead write different queries + resolvers and flexibly use them as needed. In my example above, I was thinking I could write 4 different queries + resolvers to search across 4 databases, and then be able to query all or just a subset of them as needed. As opposed to writing one long resolver that fetches from all 4 databases.
What would be the main benefit of aggregating the results into one resolver, v.s. running individual queries with corresponding resolvers? Thanks again!
Ultimately the reason to choose between the two approaches (one search field vs multiple) is going to come down to what client-side behaviour you want. If your end result is essentially going to be a single paginated list, a single field (using interfaces) is preferred. But this will make the pagination logic complex, because you need to coordinate it across data sources, which usually means fetching a full page per source and throwing away the overflow.
If you don't need this single megasearch search behaviour, what you had originally is probably a better starting point. This is definitely the kind of decision that comes from product needs rather than one approach being technically superior.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.