Nested JSON output from mongodb via Get - mongodb

I have some status-Data from a bunch of devices stored in mongoDB and I like to aggregate via mongoose some simple statistics pending on input variables e.g. input about timespan (start to end).
Plane Dataset in mongoDB looks somehowe like this:
{
"_id":"5d65b4a9cef78a5c987b2224",
"Date":"2019-08-01T00:00:00.000Z",
"Id":9,
"StandingNoMovement":21.9,
"DrivingHours":0.4,
"StandingWithEngineOn":1.6
},
{
"_id":"5d65b4a9cef78a5c987b2225",
"Date":"2019-08-02T00:00:00.000Z",
"Id":9,
"StandingNoMovement":19.2,
"DrivingHours":2.3,
"StandingWithEngineOn":2.3,
}
and I need to create a GET for an API with the structure of
[{"Id":9,
"DrivingHours":
{"Total":276.9,"Day":0.0,"ThisWeek":0.0,"ThisMonth":0.0},
"StandingNoMovement":
{"Total":678.4,"Day":0.0,"ThisWeek":0.0,"ThisMonth":0.0},
"StandingWithEngineOn":
{"Total":521.4,"Day":0.0,"ThisWeek":0.0,"ThisMonth":0.0}]
So fare, I only managed to aggregate some not nestet statistics in mongoose like:
const aggReport = mongoose.model('aggReport', aggReportingSchema);
aggReport.aggregate([
{ $match:
{ Id : req.params.Id ,'Date': { $gte: start, $lt: end}
}
},
{ $group:
{ _id: null,
'DrivingHours': {$sum: '$DrivingHours'},
'StandingWithEngineOn': {$sum: '$StandingWithEngineOn'},
'StandingNoMovement': {$sum: '$StandingNoMovement'}
}
}])
on the Schema
export const aggReportingSchema = new Schema({
Id:{ type: Number},
Date:{ type: Date},
StandingNoMovement:{ type: Number},
StandingWithEngineOn:{ type: Number},
},{ collection : 'status_daily' });
How to get the nested statistics as they look that simple to me?

I just switched now to a total different approach, as I got the impression, that there are no proper and usable solutions (perhaps with good reasons) to dynamically create such a API. So now I store all the date precalculated in the mondodb and I am using express and node just to call the necessary dataset.

Related

Use GraphQL Query to get results form MongoDB after aggregation with mongoose

so i have following problem.
I have a mongoDB collection and a corresponding mongoose model which looks like this.
export const ListItemSchema = new Schema<ListItemSchema>({
title: { type: String, required: true },
parentId: { type: Schema.Types.ObjectId, required: false },
});
export const TestSchema = new Schema<Test>(
{
title: { type: String, required: true },
list: { type: [ListItemSchema], required: false },
}
);
As you can see, my TestSchema holds an Array of ListItems inside -> TestSchema is also my Collection in MongoDB.
Now i want to query only my ListItems from a Test with a specific ID.
Well that was not that big of a problem at least from the MongoDB side.
I use MongoDB Aggregation Framework for this and call my aggregation inside a custom Resolver.
Here is the code to get an array of only my listItems from a specific TestModel
const test = TestModel.aggregate([
{$match: {_id: id}},
{$unwind: "$list"},
{
$match: {
"list.parentId": {$eq: null},
},
},
{$replaceRoot: {newRoot: "$list"}},
]);
This is the result
[ { _id: randomId,
title: 't',
parentId: null },
{ _id: randomId,
title: 'x'
parentId: null
} ]
The Query to trigger the resolver looks like this and is placed inside my Test Type Composer.
query getList {
test(testId:"2f334575196fe042ea83afbf", parentId: null) {
title
}
}
So far so good... BUT! Ofc my query will fail or will result in a not so good result^^ because GraphQL expects data based on the Test-Model but receives a completely random array.
So after a lot of typing here is the question:
How do i have to change my query to receive the list array?
Do i have to adjust the query or is it something with mongoose?
i really stuck at this point so any help would be awesome!
Thanks in advance :)
I'm not sure if I understood your issue correctly.
In your graphql, try to leave out exclamation mark(!) from the Query type.
something like :
type Query {
test: TestModel
}
instead of
type Query {
test: TestModel!
}
then you'll get the error message in console but still be able to receive any form of data.

Mongo match based on sum of array components

I have the following schema and I am having trouble returning the data that I want.
var Book = new Schema({
ISBN: String,
title: String,
author: String,
image: String,
availability: [{zipcode: String,
total: Number,
loaned: Number
}]
});
I would like to return a random sample of items (maybe 25) in which there are items available. In this case availability would be defined be the total being greater than the number loaned in at least one of the sets under "availability" (or by looking at the sum of total and loaned in all the sets). Every time I start, I seem to run into a wall. Does anyone have any idea if this is possible or how to do it?
I did finally get it working by doing this:
db.books.aggregate(
[
{
$addFields: {
available: {$gt: [{$sum: "$availability.total"}, {$sum: "$availability.loaned"}]}
}
},
{
$match: {available: true}
},
{
$sample: {size: 25}
}
]
)
I'm not sure if there are any downsides to this approach, but with limited testing it does seem to work for me.

fetching documents based on nested subdoc array value

I'm trying to get all documents in a collection based on a subdocument array values. This is my data structure in the collection i'm seeking:
{
_id: ObjectId('...'),
name: "my event",
members:
[
{
_id: ObjectId('...'),
name: "family",
users: [ObjectId('...'),ObjectId('...'),ObjectId('...')]
},
{
_id: ObjectId('...'),
name: "work",
users: [ObjectId('...'),ObjectId('...'),ObjectId('...')]
}
]
}
I should point out that the schema of these objects are defined like so:
Events:
{
name: { type: String },
members: {type: [{ type: ObjectId, ref: 'MemberGroup' }], default:[] }
}
MemberGroup:
{
name: { type: String },
users: [{type: mongoose.Schema.Types.ObjectId, ref: 'User'}]
}
and of course User is just an arbitrary object with an id.
What i'm trying to fetch: i want to retrieve all events which has a specific user id in its member.users field.
i'm not sure if its event possible in a single call but here is what i've tried:
var userId = new mongoose.Schema.ObjectId('57d9503ef10d5ffc08d6b8cc');
events.find({members: { $elemMatch : { users: { $in: [userId]} } }})
this syntax work but return no elements even though i know there are matching elements (using robomongo to visualize the db)
So after a long search in stackoverflow i came across the good answare here:
MongoDB: How to find out if an array field contains an element?
my query changed to the following which gave me my desired result:
var userId = new mongoose.Schema.ObjectId('57d9503ef10d5ffc08d6b8cc');
events.find({}, {members: { $elemMatch : { users: { $eq: userId} } }})
notice the use of the second find parameter to specify the query limit instead of the first one (which is odd)

How to use $nin to exclude array of objects in Meteor, MongoDB, React

I want to exclude array of objects from my query when fetching Object.
mixins: [ReactMeteorData],
getMeteorData() {
// Standard stuff
var selector = {};
var handle = Meteor.subscribe('videos', selector);
var data = {};
// Return data = OK!
data.video = Videos.findOne({ _id: this.props._id });
// Fetch objects with $lte _id to exclude, Return id_ field array = OK!
data.excnext = Videos.find({ votes: data.video.votes, _id: {$lt: data.video._id}},{fields: {_id:1}},{sort: {_id: 1},limit:50}).fetch();
// Fetch objects by Votes, Exclude array of objects with $nin = NOT OK!
data.next = Videos.findOne({ _id: { $ne: this.props._id, $nin:data.excnext }, votes: { $gte: data.video.votes}},{ sort: { votes: 1, _id: 1 }});
return data;
},
Why is $nin not working like expected?
Am unsure if am doing something wrong when fetching my array or when returning it using $ini
Logged example = data.excnext
[ { _id: 'A57WgS6n3Luu23A4N' },
{ _id: 'JDarJMxPAnmeTwgK4' },
{ _id: 'DqaeqTfi8RyvPPTiD' },
{ _id: 'BN5qShBJzd6N7cRzh' },
{ _id: 'BSw2FAthNLjav5T4w' },
{ _id: 'Mic849spXA25EAWiP' } ]
Grinding on my first app using this stack. My core setup is Meteor,Flow-router-ssr, React-layout, MongoDB, React. What am trying to do is to fetch next object by votes, problem is that sometimes several objects have the same amount of votes, so then i need to sort it by id and exclude unwanted objects.
First of all am seeking the answer how to use $nin correct in my example above
Second, Suggestions / examples how to do this better are welcome, There could be a much better and simpler way to do this, Information is not easy to find and without any previous experience there is a chance that am complicating this more than needed.
// ❤ peace
In this case $nin needs an array of id strings, not an array of objects which have an _id field. Give this a try:
data.excnext = _.pluck(Videos.find(...).fetch(), '_id');
That uses pluck to extract an array of ids which you can then use in your subsequent call to findOne.

MongoDB query to find property of first element of array

I have the following data in MongoDB (simplified for what is necessary to my question).
{
_id: 0,
actions: [
{
type: "insert",
data: "abc, quite possibly very very large"
}
]
}
{
_id: 1,
actions: [
{
type: "update",
data: "def"
},{
type: "delete",
data: "ghi"
}
]
}
What I would like is to find the first action type for each document, e.g.
{_id:0, first_action_type:"insert"}
{_id:1, first_action_type:"update"}
(It's fine if the data structured differently, but I need those values present, somehow.)
EDIT: I've tried db.collection.find({}, {'actions.action_type':1}), but obviously that returns all elements of the actions array.
NoSQL is quite new to me. Before, I would have stored all this in two tables in a relational database and done something like SELECT id, (SELECT type FROM action WHERE document_id = d.id ORDER BY seq LIMIT 1) action_type FROM document d.
You can use $slice operator in projection. (but for what you do i am not sure that the order of the array remain the same when you update it. Just to keep in mind))
db.collection.find({},{'actions':{$slice:1},'actions.type':1})
You can also use the Aggregation Pipeline introduced in version 2.2:
db.collection.aggregate([
{ $unwind: '$actions' },
{ $group: { _id: "$_id", first_action_type: { $first: "$actions.type" } } }
])
Using the $arrayElemAt operator is actually the most elegant way, although the syntax may be unintuitive:
db.collection.aggregate([
{ $project: {first_action_type: {$arrayElemAt: ["$actions.type", 0]}
])