Reducing output of GraphQL - graphql

I have set up a GraphQL-mongoose-express-apollo combo as per this guide.
When I run a query to get multiple results, is there a way to reduce the resulting array before I actually get to processing the response from the query.
Query:
query GetSomeUsers {
userMany (limit: 3){
_id
}
}
Actual output:
{
"data": {
"userMany": [
{
"_id": "5e950543cb48dbaafc60722d"
},
{
"_id": "5e950543cb48dbaafc60722e"
},
{
"_id": "5e950547cb48dbaafc60722f"
}
]
}
}
Desired output:
{
"data": {
"userMany": [
"5e950543cb48dbaafc60722d",
"5e950543cb48dbaafc60722e",
"5e950547cb48dbaafc60722f"
]
}
}
So far I have only found something that seems to be relevant in an article on GraphQL Leveler, but I don't see how it would work with graphql-compose-mongoose, as the GraphQL schema is automatically generated and there does not seem to be any place in the code to put in that LevelerObjectType in place of a GraphQLObjectType.

Related

Use query result as parameter for another query in Elasticsearch DSL

I'm using Elasticsearch DSL, I'm trying to use a query result as a parameter for another query like below:
{
"query": {
"bool": {
"must_not": {
"terms": {
"request_id": {
"query": {
"match": {
"processing.message": "OUT Followup Synthesis"
}
},
"fields": [
"request_id"
],
"_source": false
}
}
}
}
}
}
As you can see above I'm trying to search for sources that their request_id is not one of the request_idswith processing.message equals to OUT Followup Synthesis.
I'm getting an error with this query:
Error loading data [x_content_parse_exception] [1:1660] [terms_lookup] unknown field [query]
How can I achieve my goal using Elasticsearch DSL?
Original question extracted from the comments
I'm trying to fetch data with processing.message equals to 'IN Followup Sythesis' with their request_id doesn't appear in data with processing.message equals to 'OUT Followup Sythesis'. In SQL language:
SELECT d FROM data d
WHERE d.processing.message = 'IN Followup Sythesis'
AND d.request_id NOT IN (SELECT request_id FROM data WHERE processing.message = 'OUT Followup Sythesis');
Answer: generally speaking, neither application-side joins nor subqueries are supported in Elasticsearch.
So you'll have to run your first query, take the retrieved IDs and put them into a second query — ideally a terms query.
Of course, this limitation can be overcome by "hijacking" a scripted metric aggregation.
Taking these 3 documents as examples:
POST reqs/_doc
{"request_id":"abc","processing":{"message":"OUT Followup Synthesis"}}
POST reqs/_doc
{"request_id":"abc","processing":{"message":"IN Followup Sythesis"}}
POST reqs/_doc
{"request_id":"xyz","processing":{"message":"IN Followup Sythesis"}}
you could run
POST reqs/_search
{
"size": 0,
"query": {
"match": {
"processing.message": "IN Followup Sythesis"
}
},
"aggs": {
"subquery_mock": {
"scripted_metric": {
"params": {
"disallowed_msg": "OUT Followup Synthesis"
},
"init_script": "state.by_request_ids = [:]; state.disallowed_request_ids = [];",
"map_script": """
def req_id = params._source.request_id;
def msg = params._source.processing.message;
if (msg.contains(params.disallowed_msg)) {
state.disallowed_request_ids.add(req_id);
// won't need this particular doc so continue looping
return;
}
if (state.by_request_ids.containsKey(req_id)) {
// there may be multiple docs under the same ID
// so concatenate them
state.by_request_ids[req_id].add(params._source);
} else {
// initialize an appendable arraylist
state.by_request_ids[req_id] = [params._source];
}
""",
"combine_script": """
state.by_request_ids.entrySet()
.removeIf(entry -> state.disallowed_request_ids.contains(entry.getKey()));
return state.by_request_ids
""",
"reduce_script": "return states"
}
}
}
}
which'd return only the correct request:
"aggregations" : {
"subquery_mock" : {
"value" : [
{
"xyz" : [
{
"processing" : { "message" : "IN Followup Sythesis" },
"request_id" : "xyz"
}
]
}
]
}
}
⚠️ This is almost guaranteed to be slow and goes against the suggested guidance of not accessing the _source field. But it also goes to show that subqueries can be "emulated".
💡 I'd recommend to test this script on a smaller set of documents before letting it target your whole index — maybe restrict it through a date range query or similar.
FYI Elasticsearch exposes an SQL API, though it's only offered through X-Pack, a paid offering.

Retrieve contents of _source only- Elasticsearch ( Node JS)

According to Retrieving a document documentation
GET /website/blog/123/_source
would directly return the document stored inside the _source field.
I'm currently using Node JS's express framework. How should I implement this in my code?
esClient.search({
index: "myIndex",
type: "myType",
body: {
"query": {
"match_all": {}
},
"size": 3,
"from": 1
}
}).then(function (resp) {
var result = resp.hits.hits;
res.status(200).send({data: {recommendations: result, showItemFrom: showItemFrom}})
}, function (err) {
console.trace(err.message)
res.status(500).send({data: err.message})
})
I'm getting the response this way...
[
"_source":{
{
"id": 1,
"title": "Test"
}
}
]
However, I want it this way...
[
{
id:1,
title:"Test"
}
]
I don't think the Elasticsearch API has a method to do that for searches, the one that Val mentioned works, but it is only usable to GET documents directly through its id.
But you can map the result using the Javascript Array#map() method:
var result = resp.hits.hits.map(hit => hit._source);
After
index:"myIndex"
Add:
source:true
You need to call the getSource() function, like this:
esClient.getSource({
index: "website",
type: "blog",
id: "123"
}).then(function (source) {
// do something with source
}, function (err) {
// error happened
})

Nested pagination with relay graphql

Currently having an issue with the relay approach to nested pagination. An example below to illustrate what I mean:
{
"data": {
"locations": {
"edges": [
{
"node": {
"id": "Location_254"
}
},
{
"node": {
"id": "Location_247"
}
},
{
"node": {
"id": "Location_217"
}
},
]
}
}
Here I have 3 locations returned from a query. Now I wanted to paginate on these locations and look at their 'history'.
query {
locations {
edges {
node {
history(
first:10
after:"eyJzbm9vemVJZCI6Mzg3fQ=="
)
}
}
}
}
This would paginate 10 results after the specified cursor. My issue is, is that this cursor is specific to the location it was obtained from. The cursor it is referring to paginate after, only applies to the location it came from.
Nested pagination tries to paginate on ALL locations here, when in actuality, the cursor being used, was grabbed from a specific location.
Am I seeing this incorrectly, or is there a better way I could be approaching this issue?
Regards, Sebastian

GraphQL Github API formatting

I am wondering how to deal with the following problem. I am using GraphQL to query the v4 Github API with the following query:
{
viewer {
repositories(first: 30) {
edges {
node {
name
}
}
}
}
}
This gets me a response that looks like so:
{
"data": {
"viewer": {
"repositories": {
"edges": [
{
"node": {
"name": "test-repo"
}
},
{
"node": {
"name": "another-repo"
}
}
]
}
}
}
}
I am pretty new to GraphQL, I understand that in my query I need to provide the edges and nodes but I would rather get a response back in this kind of way because I am not interested to know about "edges" and "nodes" in my frontend:
{
"data": {
"viewer": {
"repositories": [
{
"name": "test-repo"
},
{
"name": "another-repo"
}
]
}
}
}
}
I am guessing this kind of response is normal for GraphQL but it would be pretty cumbersome to rewrite to response all the time for easier usage in my frontend. Is there some way to emit the "edges" and "nodes" and get the formatting that I would like or is this simply all up to me to deal with?
I have looked at some libraries like Apollo but I have no idea is this is a right fit to deal with things like this. Hopefully someone a bit more experienced with GraphQL could tell me something more.
Sometimes, services provides two endpoints: Relay endpoint (with edges and nodes) and simple endpoint.
Looks like GitHub only have a Relay endpoint. In this case, the only thing you can do is to manually format the response on your frontend.
Actually, such complex response structure is needed because we often need to do a pagination. Take a look at the example:
{
getArticle(id: "some-id") {
id
userId
user {
id
name
}
tags(first: 10, after: "opaqueCursor") {
edges {
node {
id
name
itemsCount
}
}
pageInfo {
hasNextPage
hasPreviousPage
endCursor
startCursor
}
}
}
}
pageInfo is located at the same level as edges.
So if you later will need to do a pagination, it would be better to keep the response format as is.
You can remove the edges query if you know you aren't searching along those relationships. Cursor-based pagination will work by checking the pageInfo value hasNextPage and using endCursor as the after query parameter:
viewer {
repositories(first: 30,after:"<CURSOR_STRING>") {
totalCount
pageInfo{
hasNextPage
endCursor
}
nodes{
name
}
}
}
returns
"viewer": {
"repositories": {
"totalCount": 38,
"pageInfo": {
"hasNextPage": true,
"endCursor": "Y3Vyc29yOnYyOpHOAl/5mw=="
},
"nodes": [
{
"name": "AllStarRoom"
},
{
"name": "shimsham"
},
{
"name": "Monitor-Docs"
}
]
}
}

Spring MongoDB distict - can't get full document

I have collection in following format.
{
id:____,
name:"Carlos",
city:"Mumbai"
},
{
id:____,
name:"Pravin",
city:"Mumbai"
},
{
id:_____,
name:"Gaurav",
city:"Ahmedabad"
}
I want whole document distinct by city. I tried the db.collection.distinct("city"). But it returns only distinct cities.
Current Output:
["Mumbai","Ahmedabad"]
Expected Output:
{
id:____,
name:"Carlos",
city:"Mumbai"
},
{
id:_____,
name:"Gaurav",
city:"Ahmedabad"
}
Above you can see there is only one record of "Mumbai". I need this kind of output.
Anyone know how we can get whole document with distinct in spring-mongodb?
You could try running an aggregation pipeline operation where you can include the the other fields inside the $group pipeline stage using the $first operator. Two examples that show this approach follow:
Mongo Shell:
pipeline = [
{
"$group": {
"_id": "$city",
"id": { "$first": "$_id" },
"name": { "$first": "$name" }
}
}
]
db.collection.aggregate(pipeline);
Spring Data MongoDB:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
MongoTemplate mongoTemplate = repository.getMongoTemplate();
Aggregation agg = newAggregation(
group("city")
.first("_id").as("id")
.first("name").as("name")
);
AggregationResults<OutputType> result = mongoTemplate.aggregate(agg,
"collection", OutputType.class);
List<OutputType> mappedResult = result.getMappedResults();

Resources