Converting mongoshell projection to Spring MongoTemplate projection - spring

Given following mongoshell query - which works fine
[
{
'$match': {
'id': '1'
}
}, {
'$graphLookup': {
'from': 'pages',
'startWith': '$cID',
'connectFromField': 'parent',
'connectToField': 'cID',
'as': 'result',
'depthField': 'level'
}
}, {
'$unwind': {
'path': '$result',
'preserveNullAndEmptyArrays': true
}
}, {
'$sort': {
'result.level': 1
}
}, {
'$group': {
'_id': '$id',
'result': {
'$push': '$result'
}
}
}, {
'$project': {
'result': {
'id': 1,
'cID': 1,
'level': 1
}
}
}
]
How do I achieve the projection using the Spring MongoTemplate and the its given DSL?
Given the source and the AggregationTests provided by the github repo, I could be
project().and("result").nested(Fields.fields("id","cID","level")
which just returns an empty result list.

I didn't come up with a solution to make it work with the MongoTemplate directly. As I'm already using the MongoRepository given by Spring Data, creating an aggregation query is quite straightforward.
public interface Repository extends MongoRepository<Object, String> {
#Aggregation(pipeline = {
"{'$match': {'cID': '?0'}}",
"{'$graphLookup': {'from': 'pages', 'startWith': '$cID', 'connectFromField': 'cID', 'connectToField': 'parent', 'as': 'result', 'depthField': 'level'}}",
"{'$unwind': {'path': '$result', 'preserveNullAndEmptyArrays': true}}",
"{'$sort': {'result.level': -1}}",
"{'$group': {'_id': '$id', 'result': {'$push': '$result'}}}",
"{'$project': {'cID_list': '$result.cID', 'result': {'level': 1, 'cID': 1, 'id': 1}}}"
})
AggregationResults<Map<?, List<String>>> getChildrencIDs(final String cID);
}

Related

How to implement Flutter api json data to a list view at 3rd level category?

I'm trying to build a flutter app, the idea is to have:
List of Hospitals and a Hospital has many doctors.
Doctors are categories by departments, Example: Cardiology department, Neurology department.
The idea is to have a list of hospitals list from api json data, after clicking a specific hospital, there should be list of departments (api json data) and after selecting a specific department from the list - it will load all the doctors in a list from that department.
Can anyone help please? I'm using Laravel 9 as a backend.
don't know if it can help you
- Json code
{
"hospital":[
{
"name": "hospital1",
"department":[
{
"name": "department1",
"doctors":[
{
"name": "doctor1"
},
{
"name": "doctor2"
}
]
},
{
"name": "department2",
"doctors":[
{
"name": "doctor1"
},
{
"name": "doctor2"
}
]
}
]
},
{
"name": "hospital2",
"department":[
{
"name": "department1",
"doctors":[
{
"name": "doctor1"
},
{
"name": "doctor2"
}
]
},
{
"name": "department2",
"doctors":[
{
"name": "doctor1"
},
{
"name": "doctor2"
}
]
}
]
}
]
}
- Dart code
class DataResponse{
DataResponse({
this.hospital,
});
List<Hospital> hospital;
factory DataResponse.fromJson(Map<String, dynamic> json) => Welcome(
hospital: List<Hospital>.from(json["hospital"].map((x) => Hospital.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"hospital": List<dynamic>.from(hospital.map((x) => x.toJson())),
};
}
class Hospital {
Hospital({
this.name,
this.department,
});
String name;
List<Department> department;
factory Hospital.fromJson(Map<String, dynamic> json) => Hospital(
name: json["name"],
department: List<Department>.from(json["department"].map((x) => Department.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"name": name,
"department": List<dynamic>.from(department.map((x) => x.toJson())),
};
}
class Department {
Department({
this.name,
this.doctors,
});
String name;
List<Doctor> doctors;
factory Department.fromJson(Map<String, dynamic> json) => Department(
name: json["name"],
doctors: List<Doctor>.from(json["doctors"].map((x) => Doctor.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"name": name,
"doctors": List<dynamic>.from(doctors.map((x) => x.toJson())),
};
}
class Doctor {
Doctor({
this.name,
});
String name;
factory Doctor.fromJson(Map<String, dynamic> json) => Doctor(
name: json["name"],
);
Map<String, dynamic> toJson() => {
"name": name,
};
}

Query to get top 10 users from MongoDb in Spring

So basically I have a collection that looks like this(other fields omitted):
[{
user: mail1#test.com
},
{
user: mail1#test.com
},
{
user: mail1#test.com
},
{
user: mail2#test.com
},
{
user: mail2#test.com
},
{
user: mail3#test.com
}
]
I'm looking for a way to query MongoDB in order to get the top 10 active users(those with the most records in DB). Is there an easy way to get this, perhaps just using the interface?
perhaps a simple group aggregation will give you the needed result?
db.Users.aggregate(
[
{
$group: {
_id: "$user",
count: { $sum: 1 }
}
},
{
$sort: { count: -1 }
},
{
$limit: 10
},
{
$project: {
user: "$_id",
_id: 0
}
}
])
There is something called $sortByCount for aggregation.
List<UserCount> getTop10UserCount() {
return mongoTemplate.aggregate(
newAggregation(
User.class,
sortByCount("user"),
limit(10),
project("_id", "count")
),
UserCount.class
);
}
static class UserCount {
String _id;
Integer count;
// constructors, getters or setters..
}

Cannot pass custom result from resolver to Graphql

I am trying to fetch data with sequelize with an attribute and pass it to graphql.
The result is fine in console but the graphql query is returning null for the attribute field.
my resolver
getUnpayedLessons: async (_, args, { models }) => {
const { Attendance, Student } = models;
return await Attendance.findAll({
include: {
model: Student,
},
where: {
fk_lessonsSerieId: { [Op.is]: null },
},
attributes: ["id", [sequelize.fn("count", sequelize.col("absenceFlag")), "unpayedLessons"]],
group: ["student.id"],
});
},
query
getUnpayedLessons {
id
unpayedLessons
student {
id
firstName
lastName
}
}
schema
type UnpayedLessons {
id: Int
unpayedLessons: Int
student: Student
}
extend type Query {
getUnpayedLessons: [UnpayedLessons]
}
and this is the console.log of the resolver when I run the query
[
attendance {
dataValues: { id: 2, unpayedLessons: 8, student: [student] },
_previousDataValues: { id: 2, unpayedLessons: 8, student: [student] },
_changed: Set {},
_options: {
isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
include: [Array],
includeNames: [Array],
includeMap: [Object],
includeValidated: true,
attributes: [Array],
raw: true
},
]
and from graphql
{
"data": {
"getUnpayedLessons": [
{
"id": 2,
"unpayedLessons": null,
"student": {
"id": 2,
"__typename": "Student"
},
"__typename": "UnpayedLessons"
},
]
}
}
Any idea how I can have unpayedLessons passed to graphql?
To debug this you need to check what is returned from DB, the shape:
const values = await Attendance.findAll({...
console.log( values );
// adapt structure to match query requirements
// finally return
return values;

Apollo Server / GraphQL - Properties of Nested Array Returning Null

Bear with me, I will explain this the best I can. Please let me know if more information is needed, I am trying to keep this as brief as possible.
I am using Apollo Server and the 'apollo-datasource-rest' plugin to access a REST API. When attempting to get the property values from a nested array of objects I get a null response for each field/property. In addition, the array being queried is only showing a single iteration when multiple are available.
The field in question is the 'cores' field within the Rocket type, i.e., launch.rocket.firstStage.cores
I have attempted various ways of mapping through 'cores' (thinking this was what it wanted) with no success.
To keep things short and simple I'm only including the code for the specific issue. All other parts of the query are operating as expected.
You can view the API response I am hitting here: https://api.spacexdata.com/v3/launches/77
schema.js
const { gql } = require('apollo-server');
const typeDefs = gql`
type Query {
singleLaunch(flightNumber: Int!): Launch
}
type Launch {
flightNumber: Int!
rocket: Rocket
}
type Rocket {
firstStage: Cores
}
type Cores {
cores: [CoreFields]
}
type CoreFields {
flight: Int
gridfins: Boolean
legs: Boolean
reused: Boolean
landingType: String
landingVehicle: String
landingSuccess: Boolean
}
`;
module.exports = typeDefs;
Data Source - launch.js
const { RESTDataSource } = require('apollo-datasource-rest');
class LaunchAPI extends RESTDataSource {
constructor() {
super();
this.baseURL = 'https://api.spacexdata.com/v3/';
}
async getLaunchById({ launchId }) {
const res = await this.get('launches', {
flight_number: launchId,
});
return this.launchReducer(res[0]);
}
launchReducer(launch) {
return {
flightNumber: launch.flight_number || 0,
rocket: {
firstStage: {
cores: [
{
flight: launch.rocket.first_stage.cores.flight,
gridfins: launch.rocket.first_stage.cores.gridfins,
legs: launch.rocket.first_stage.cores.legs,
landingType: launch.rocket.first_stage.cores.landing_type,
landingVehicle: launch.rocket.first_stage.cores.landing_vehicle,
landingSuccess: launch.rocket.first_stage.cores.landing_success,
},
],
},
};
}
}
module.exports = LaunchAPI;
resolvers.js
module.exports = {
Query: {
singleLaunch: (_, { flightNumber }, { dataSources }) =>
dataSources.launchAPI.getLaunchById({ launchId: flightNumber }),
},
};
Query
query GetLaunchById($flightNumber: Int!) {
singleLaunch(flightNumber: $flightNumber) {
flightNumber
rocket {
firstStage {
cores {
flight
gridfins
legs
reused
landingType
landingVehicle
landingSuccess
}
}
}
}
}
Expected Result
{
"data": {
"singleLaunch": {
"flightNumber": 77,
"rocket": {
"firstStage": {
"cores": [
{
"flight": 1,
"gridfins": true,
"legs": true,
"reused": true,
"landingType": "ASDS",
"landingVehicle": "OCISLY",
"landSuccess": true,
},
{
"flight": 1,
"gridfins": true,
"legs": true,
"reused": false,
"landingType": "RTLS",
"landingVehicle": "LZ-1",
"landSuccess": true
},
{
"flight": 1,
"gridfins": true,
"legs": true,
"reused": false,
"landingType": "RTLS",
"landingVehicle": "LZ-2",
"landSuccess": true
},
]
}
},
}
}
}
Actual Result (Through GraphQL Playground)
{
"data": {
"singleLaunch": {
"flightNumber": 77,
"rocket": {
"firstStage": {
"cores": [
{
"flight": null,
"gridfins": null,
"legs": null,
"reused": null,
"landingType": null,
"landingVehicle": null,
"landingSuccess": null
}
]
}
},
}
}
}
Any suggestions as to what I am doing wrong here would be greatly appreciated. Again, let me know if more information is needed.
Thank you!
Missing base url
There should be
await this.get( this.baseURL + 'launches'
IMHO there should be a map used within launchReducer to return an array, sth like:
launchReducer(launch) {
return {
flightNumber: launch.flight_number || 0,
rocket: {
firstStage: {
cores: launch.rocket.first_stage.cores.map(core => ({
flight: core.flight,
gridfins: core.gridfins,
legs: core.legs,
landingType: core.landing_type,
landingVehicle: core.landing_vehicle,
landSuccess: core.land_success,
})),
},
},
};
}
.map(core => ({ is for returning object [literal], the same as/shorter version of .map(core => { return {

Use Bluebird to deep populate objects in Sailsjs?

There are two popular and similar questions to mine, but the difference is that those only have to worry about deep populating associations for one object, whereas mine is about N objects.
Suppose I have 3 models defined as such (left out some attributes for clarity):
identity: 'room',
attributes: {
LocationId : { type: 'integer',
primaryKey: true,
required: true,
autoIncrement: true },
DisplayName : { type: 'string',
unique: true },
FloorId : { model: 'Floor' }
}
identity: 'floor',
attributes: {
FloorId : { type: 'integer',
primaryKey: true },
FloorName : { type: 'string' },
BuildingId : { model: 'Building' },
rooms: {collection:'room', via:'FloorId'}
}
identity: 'building',
attributes: {
BuildingId : { type: 'integer',
primaryKey: true },
BuildingName : { type: 'string' },
floors: {collection:'floor', via:'BuildingId'}
}
The end goal is to have an array of objects that has this basic structure:
[{
"LocationId": 555,
"DisplayName": 'SomeCoolName',
"Floor" : {
"FloorId": 1337,
"FloorName": '5',
"Building": {
"BuildingId": 4321,
"BuildingName": 'HQ'
}
}
}, {...}]
I've not got far due to not knowing the BlueBird library promises as well as I should:
showWithAssetGeo: function(req, res) {
room.find( { assetCount: { '>': 0 } } )
.populate('FloorId')
.each(function(room){
var Building = Building.find({ id: _.pluck(room.FloorId, 'BuildingId') })
.then(function(Building) {return Building;});
return [room, Building];
})
.spread(function(room, Building) {
//Something to combine it all?
})
.catch (function(err) {
if (err) { res.badRequest('reason' + err); }
}
}
UPDATE: Had to tweak the answer marked below. Here is the final working code.
You need to make sure to execute the find by calling then or exec (each won't do it).
Seems like you're trying to map across all the floors and then bring those promises back to one. Promise.all() is the way to do that.
Try something like the below:
showWithAssetGeo: function(req, res) {
room.find( { assetCount: { '>': 0 } } )
.populate('FloorId')
.then(function(rooms) {
return Promise.all(rooms.map(function(room) {
return Building.findOne({id: room.FloorId.BuildingId})
.then(function(building) {
room.FloorId.building = building;
});
})
})
.then(function(deeplyPopulatedRooms) {
res.json(deeplyPopulatedRooms);
})
.catch(function(error) {
if (err) { res.badRequest('reason' + err); }
});
}
However, it would probably be more performant to pluck all the id's of the possible buildings and do one find for all id's. But the above should work and seems to be consistent with the approach you were taking before.

Resources