Spring-boot data mongoDB query nested list - mongodb

I'm working on spring-boot-data-mongoDB. I have some issues querying a nested document that has a list of a specific object.
Mock class
#Document
public class Mock {
#Id
private String id;
#Indexed(unique = true)
private String name;
private List<Request> requests;
}
Request class
#Document
public class Request {
#Id
private String id;
private int status;
private String method;
private String endPoint;
private Map<String, Object> response;
private Map<String, Object> body;
private Map<String, String> params;
}
Example JSON
[
{
_id: '53fc6dde-7a534-4b37-a57e-t0bd62f50046',
name: 'mock1',
requests: [
{
status: 200,
method: 'GET',
endPoint: 'status',
response: {},
body: {},
params: {}
}
],
_class: 'com.example.mockserverspring.models.Mock'
},
{
_id: '73fc6dde-7a5b-4b37-a57e-d0bd62f50046',
name: 'tester',
requests: [
{
_id: '802220ea-a1c7-484d-af1b-86e29b540179',
status: 200,
method: 'GET',
endPoint: 'api',
response: {
data: 'GET'
},
body: {
body: 'body'
},
params: {
params: 'params'
}
},
{
_id: 'ff8673d7-01a9-4d6f-a42e-0214a56b227b',
status: 200,
method: 'GET',
endPoint: 'data',
response: {},
body: {
data: 'data'
},
params: {
value: '10'
}
},
{
_id: '7fd5a860-b415-43b0-8115-1c8e1b95c3ec',
status: 200,
method: 'GET',
endPoint: 'status',
response: {},
body: {},
params: {}
}
],
_class: 'com.example.mockserverspring.models.Mock'
}
]
Desired query output : pass in the endPoint, mockName, body, params, and method
Get the mock object of the mockName from the db.
Match endPoint, body, params, method inside the Requests List of the returned mock.
Return the response field from the request that is found matching all the above criteria.
From the above example json :
Passed in values : mockName : tester , method : GET , endPoint : api , body: {body: 'body' }, params: { params: 'params' }
This should return :
response: { data: 'GET' }
It should return if and only if all these criteria matches.
Any queries please let me know.

To perform this search the best is to use a mongoDB aggregation, inside this aggregation we will be able to execute operations step by step.
As you want to query only 1 subdocument within an array, the first operation we must perform is a $unwind of that array. This will separate each subdocument and we can perform our search.
{
"$unwind": "$requests"
}
Now we will introduce the search parameters in $match. We will be able to use as many as we want.
{
"$match": {
"name": "tester",
"requests.method": "GET",
"requests.endPoint": "api",
"requests.body": {
body: "body"
},
"requests.params": {
params: "params"
}
}
}
Finally as we only want the information of a specific field we will use $replaceRoot to format our output.
{
"$replaceRoot": {
"newRoot": "$requests.response"
}
}
Playground

EDIT:
What you trying to match by?
Generally APIs/endpoints when called via REST protocol work like this:
Request => <= Response
So I make a req and I get back a res - whether it's good or not I like it or not I get it back. Whether my params match or not.
What I can't understand is the whole design and what you're trying to match by i.e. which params? How can we match if no values exist in request LIST?
I think there are a lot of questions to be answered first. But here is some help and how I would start designing this:
Freestyle it or use Swagger to first design Req and Resp objects (schemas) - THESE ARE NOT your Requests above. These is the basic API design
Two - define what needs to happen when a request is made, with what arguments and expect what values to do the condition checking with
Define what you expect back - what fields, etc.
Define testing all of the first 3 points above individually and end to end
Then you can use each of the items in Request to test your API with. It's also fair straight forward to pull items in/out of cloud mongo service such as mongodb.com and with express easy to do the REST.

Related

GraphQL - resolve JSON as array of graphql types to support batch querying

I've got a collection of graphql services that are combined together in a gateway using graphql-tools schema stitching.
Here's some sample code for reference:
async function makeGatewaySchema() {
// define the remote executors, introspect the subschemas, etc.
return stitchSchemas({
subschemas: [
{
schema: postSubschema,
executor: postExecutor,
batch: true,
},
{
schema: userSubschema,
executor: userExecutor,
batch: true,
},
],
typeDefs: `
...
extend type User {
posts: [Post]
batchPostsJSON: JSON
batchPosts: [Post]
}
...
`,
resolvers: {
User: {
posts: {
selectionSet: "{ id }",
resolve(user, args, context, info) {
return delegateToSchema({
schema: schemaPost,
operation: "query",
fieldName: "postsByUserId",
args: { id: user.id },
context,
info,
});
},
},
batchPostsJSON: {
selectionSet: "{ id }",
resolve(user, args, context, info) {
return batchDelegateToSchema({
schema: schemaFavorite,
operation: "query",
fieldName: "postsByUserIds",
key: user.id,
argsFromKeys: (ids) => ({ ids }),
valuesFromResults: (results, keys) => {
return keys.map((id) => {
return results.filter((f) => f.user_id === id);
});
},
context,
info,
});
},
},
batchPosts: {
selectionSet: "{ id }",
resolve(user, args, context, info) {
return batchDelegateToSchema({
schema: schemaPost,
operation: "query",
fieldName: "postsByUserIds",
key: user.id,
argsFromKeys: (ids) => ({ ids }),
valuesFromResults: (results, keys) => {
// doesn't matter, we don't get here
},
context,
info,
});
},
},
},
},
});
}
In the example code above, I have three ways to get posts associated with a user:
1: User.posts
This works fine but isn't batched
2: User.batchPostsJSON
This is batched very nicely and works perfectly with one problem: the JSON return type doesn't allow me to query Post fields - I just get all of them no matter what. Worse, if Post was related to some third type, I wouldn't be able to follow that relationship.
3: User.batchPosts
This allows me to query fields of Post, but throws an exception - I've defined the return type as array of Post, but JSON is what comes back and I get an error in the gateway.
Is there a way to handle the JSON returned by the subschema and pretend that I'm really getting [Post] back instead? By the time my valueFromResults is finished, that IS what it will look like. The problem is that the return type mismatch between my typedef and the actual resolver throws an error before I have the chance to reformat the return value.

Why I'm I getting an error saving date using graphql > hasura > postgres

I'm using react, apollo, graphql, hasura, postgres as my stack to interact with the database. I think my issue is something small, so I'll just focus on the part that's not working rather than posting the whole code.
Thanks.
Error: GraphQL error: unexpected variables in variableValues: birthday
at new ApolloError (bundle.esm.js:63)
at Object.next (bundle.esm.js:1004)
at notifySubscription (Observable.js:135)
at onNotify (Observable.js:179)
at SubscriptionObserver.next (Observable.js:235)
at bundle.esm.js:866
at Set.forEach (<anonymous>)
at Object.next (bundle.esm.js:866)
at notifySubscription (Observable.js:135)
at onNotify (Observable.js:179)
at SubscriptionObserver.next (Observable.js:235)
at bundle.esm.js:76
variables{ id: 2324324, name: "Fred", birthday: "1991-01-11" }
If i remove birthday the query works.
Here is the function
const onUpdateUser = (options) => {
updateUser({
variables: Object.assign({ id: userId }, options),
optimisticResponse: {
__typename: "mutation_root",
update_users: {
__typename: "users_mutation_response",
affected_rows: 1,
returning: [
{
__typename: "users",
id: userId,
...options,
},
],
},
},
});
};
input {birthday: '1991-01-11'}
So without looking at your graphql query, I think you may be thinking of it a little bit off.
You can't dynamically add non-existent variables to a graphql query. The error is telling you that you are trying to add a variable that doesn't exist in your query
i.e. this with NOT work because you haven't defined birthday.
mutation updateUser(
$userId: Int!
$birthday (UNDEFINED)
) {
rest of query...
}
If you need to add a dynamic amount of variables, you could do something like this.
React Code
const onUpdateUser = (options) => {
updateUser({
variables: {
userId,
userVariables: options
},
optimisticResponse: {
__typename: "mutation_root",
update_users: {
__typename: "users_mutation_response",
affected_rows: 1,
returning: [
{
__typename: "users",
id: userId,
...options,
},
],
},
},
});
};
GraphQL mutation
mutation updateUser(
$userId: Int!
$userVariables: user_set_input!
) {
update_user(
where: { id: { _eq: $userId} }
_set: $userVariables
) {
affected_rows
}
}
https://hasura.io/docs/1.0/graphql/manual/mutations/update.html

Distinct Query with Cloudant Connector using Loopback in API Connect/StrongLoop

I am trying to get distinct values for a query using Loopback with a Cloudant Connector, but I haven't found anything about this in the documentation.
e.g. I need a query to turn this:
[
{
rating: "★★★★★"
},
{
rating: "★★★★★"
},
{
rating: "★★★★★"
},
{
rating: "★★★★★"
},
{
rating: "★★★☆☆"
},
{
rating: "★★★☆☆"
}
]
into this:
[
{
rating: "★★★★★"
},
{
rating: "★★★☆☆"
}
]
I'm using the REST API to query my Products model (above is a filtered view of just the rating field). If there is some sort of filter that I can use without modifying the server that I somehow just missed in the documentation, that would be the best choice.
Is there any way I can add a distinct field like:
/Products?filter[fields][rating]=true?distinct=true
or how can I go about solving this?
Also, I've seen another answer talking about adding a remote method to solve this (something like this for mySQL):
Locations.regions = function (cb) {
var ds = Locations.app.datasources.myDS;
var sql = "SELECT DISTINCT region FROM Locations ORDER BY region"; // here you write your sql query.
ds.connector.execute(sql, [], function (err, regions) {
if (err) {
cb(err, null);
} else {
cb(null, regions);
}
});
};
Locations.remoteMethod(
'regions', {
http: {
path: '/regions',
verb: 'get'
},
returns: {
root: true,
type: 'object'
}
}
);
If this would work, how would I implement it with the Cloudant NoSQL DB connector?
Thanks!
If your documents looked like this:
{
"name": "Star Wars",
"year": 1978,
"rating": "*****"
}
You can create a MapReduce view, which emits doc.rating as the key and uses the build-in _count reducer:
function(doc) {
emit(doc.rating,null);
}
When you query this view with group=true, distinct values of rating will be presented with counts of their occurrence in the data set.

Mongoose Aggregate Return to REST API - Cast to ObjectId failed for value

I am trying to set the result of my aggregate call from mongodb to a rest api endpoint. The goal is to get the count of the number of times each manager is in the collection. Below are my dependencies:
"body-parser": "^1.14.2",
"express": "^4.13.4",
"mongoose": "^4.4.2",
"nodemon": "^1.8.1",
Mongoose Schema:
var projectSchema = new Schema({
client: String,
project: String,
series: String,
manager: String,
pieceCount: Number,
dateShipped: Date,
comments: String,
items: [{description: String, barcode: Number, quantity: Number, box:
Number,returned: Boolean, dateReturned: Date}]});
Project = mongoose.model('Project', projectSchema);
Aggregate Call:
var getTotalManagers = function () {
Project.aggregate([
{ $group: {
_id: '$manager',
"total": {$sum:1}}}],
function (err, res) {
if (err) {
console.log(err);
} else {
console.log(JSON.stringify(res));
/* return (JSON.stringify(res)); (what is used when not writing to
console */
}
});
};
getTotalManagers();
This returns the desired results to the console:
[{"_id":"Fname Lname","total":1},{"_id":"Fname Lname","total":1},
{"_id":"Fname Lname","total":4},{"_id":"Fname Lname","total":2},
{"_id":"Fname Lname","total":2}]
Rest API Call:
router.get('/totals',getTotalManagers);
I get this error message when trying to access the api call:
{
"message": "Cast to ObjectId failed for value \"totals\" at path \"_id\"",
"name": "CastError",
"kind": "ObjectId",
"value": "totals",
"path": "_id"
}
What is the proper way to set this to a rest api endpoint? I feel like that is the issue since the aggregate call is working to the console. Any help would be appreciated. Thanks.

Error while setting up Extjs Rest Proxy Create function

I am using Rest proxy in Extjs Model as:
Ext.define('ThemeApp.model.peopleModel', {
extend: 'Ext.data.Model', fields: [
{ name: 'userId' },
{ name: 'title' },
{ name: 'body'}
],
proxy: {
type: 'rest',
format: 'json',
limitParam:"",
filterParam: "",
startParam:'',
pageParam:'',
url:'http://jsonplaceholder.typicode.com/posts/1',
api: {
read : 'http://jsonplaceholder.typicode.com/posts/1',
create: 'http://httpbin.org/post'},
headers: {'Content-Type': "application/json" },
reader: {
type: 'json',
//rootProperty:'issues'
},
writer: {
type: 'json'
}
In my view I am calling create function as:
var user = Ext.create('posts', {"userId": 124,"title": "sunt","body": "quia"});
user.save();
As I am testing everything on http://jsonplaceholder.typicode.com/ so I am expecting that code will work cause when I test GET and POST functionality via Postman utility everything works fine.
Can anyone point out my error?
I found my mistake.
In the following code I was not setting the correct name of my model, as it won't be "Posts"
var user = Ext.create('posts', {"userId": 124,"title": "sunt","body": "quia"});
user.save();
Also if you are trying with http://jsonplaceholder.typicode.com/ you are not supposed to send ID in the post request.