What improvements I can make on student attendance NoSQL database? - database

I’m designing a NoSQL database for student attendance system, I want your advices to improve it since I’m new to this field.
I want to query 1. classes for specific student, 2. students on specific class, 3. Attendees for specific class on specific date, 4. attendance and absence count for specific student on specific class.
{
"attendance" : {
"CS 331" : {
"7-3-2019" : {
"2014901001" : true
}
}
},
"class" : {
"class01" : {
"id" : "CS 331",
"name" : "Software Design",
"students" : {
"2014901001" : true
}
}
},
"classEnrollment" : {
"CS 331" : {
"2014901001" : {
"absence" : 0,
"attendant" : 1
}
}
},
"instructor" : {
"instructor01" : {
"id" : "01"
}
},
"instructorEnrollment" : {
"01" : {
"CS 331" : true
}
},
"student" : {
"student01" : {
"id" : "2014901001",
"name" : "Paul Howard"
}
},
"studentEnrollment" : {
"2014901001" : {
"CS 331" : true
}
}
}

This what i will design for such a user requirement
{
"class_collection": {
"documents": [
{
"id": "CS_331_2019_1",
"type": "CS 331",
"name": "Software Design",
"enrolled_instructor": [
"instructor_id_1"
],
"enrolled_students": [
{
"id": "student_id_1",
"absence": 0,
"attendant": 2
},
{
"id": "student_id_2",
"absence": 2,
"attendant": 3
}
]
}
]
},
"class_attendance_collection": {
"documents": [
{
"2019-03-12T18:00:00": {
"class_id": "CS_331_2019_1",
"attended": ["student_id_1"],
"not_attended": ["student_id_2"]
}
},
{
"2019-03-13T16:00:00": {
"class_id": "CS_331_2019_1",
"attended": ["student_id_1","student_id_2"]
}
}
]
},
"student_collection": {
"documents": [
{
"id": "student_id_1",
"name": "name_1"
},
{
"id": "student_id_2",
"name": "name_2"
}
]
},
"instructor_collection": {
"documents": [
{
"id": "instructor_id_1",
"name": "name_1"
},
{
"id": "instructor_id_2",
"name": "name_2"
}
]
}
}
Have fun coding :)

Related

Nested Query on Array in MongoDB collection

I have below User collection and I want find/update query to update nested array elements.
{
_id:"000-0000-0001",
Roles :{
0000-0000-0011: //EngagementId
["0000-0000-0111", "0000-0000-0112", "0000-0000-3333"],//RoleId
"0000-0000-0012" :
["0000-0000-0121", "0000-0000-0112"]
}
},
{
_id:"000-0000-0002",
Roles :{
"0000-0000-0021" : [ "0000-0000-0222", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112", "0000-0000-3333"]
}
}
Requirement: I want to pull RoleId 0000-0000-3333 if the array have combination of 0000-0000-3333 and 0000-0000-0112
Below is expected result :
{
_id:"000-0000-0001",
Roles :{
"0000-0000-0011" : ["0000-0000-0111", "0000-0000-0112"],
"0000-0000-0012" : ["0000-0000-0121", "0000-0000-0112"]
}
},
{
_id:"000-0000-0002"
Roles :{
"0000-0000-0021" : [ "0000-0000-0222", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"]
}
}
Note : Find/update will work on Key:value or if it is nested then key.key:value, but in above example we have key.value.[values]:$pull(value) and that's the challaenge.
The data model necessitates a complicated update with a pipeline.
Here' one way to do it.
db.collection.update({
"Roles": {"$exists": true}
},
[
{
"$set": {
"Roles": {
"$arrayToObject": {
"$map": {
"input": {"$objectToArray": "$Roles"},
"as": "roleKV",
"in": {
"k": "$$roleKV.k",
"v": {
"$cond": [
{
"$and": [
{"$in": ["0000-0000-0112", "$$roleKV.v"]},
{"$in": ["0000-0000-3333", "$$roleKV.v"]}
]
},
{
"$filter": {
"input": "$$roleKV.v",
"cond": {"$ne": ["$$this", "0000-0000-3333"]}
}
},
"$$roleKV.v"
]
}
}
}
}
}
}
}
],
{"multi": true}
)
Try it on mongoplayground.net.

Formatting dates with $dateToString in nested arrays

I have a collection of documents where each document represents a location of charging stations. I'm trying to use $map and $dateToString to transform each last_updated field into a string and drop the milliseconds portion of the datetime. There can be up to 3 last_updated fields on a location. A top level last_updated, a last_updated for each evse within an evses array, and a last_updated field within each connector of each evse. It is also possible for the evses array to not be present.
Here is an example of 2 locations:
[
{
"id" : "A",
"name" : "Meades Park",
"evses" : [
{
"uid" : "B",
"connectors" : [
{
"id" : "C",
"standard" : "IEC_62196_T1",
"last_updated" : ISODate("2021-02-18T23:54:56.000Z")
}
],
"last_updated" : ISODate("2021-02-18T23:54:56.000Z")
},
{
"uid" : "D",
"connectors" : [
{
"id" : "E",
"standard" : "IEC_62196_T1",
"last_updated" : ISODate("2021-02-18T23:54:56.000Z")
}
],
"last_updated" : ISODate("2021-02-18T23:54:56.000Z")
}
],
"last_updated" : ISODate("2021-12-14T23:42:06.000Z")
},
{
"id" : "F",
"name" : "5th Avenue",
"last_updated" : ISODate("2022-01-12T13:12:01.000Z")
}
]
I have my query working for the top level last_updated field and the evses.last_updated field, but I am having trouble modifying the evses.connectors.last_updated field. This is my query:
db.collection.aggregate([
{
$addFields: {
last_updated: { $dateToString: { format: '%Y-%m-%dT%H:%M:%SZ', date: '$last_updated' } },
evses: {
$map: {
input: '$evses',
as: 'evse',
in: {
{
$mergeObjects: [
'$$evse',
{ last_updated: { $dateToString: { format: '%Y-%m-%dT%H:%M:%SZ', date: '$$evse.last_updated' } } }
]
}
}
}
}
}
}
])
Is it possible to do another $map within the $map.in object? I was able to get this working by turning in into an array, but this changed the way the data was returned.
Here is an example of my query using $map.in as an array:
db.collection.aggregate([
{
$addFields: {
last_updated: { $dateToString: { format: '%Y-%m-%dT%H:%M:%SZ', date: '$last_updated' } },
evses: {
$map: {
input: '$evses',
as: 'evse',
in: [
{
$mergeObjects: [
'$$evse',
{ last_updated: { $dateToString: { format: '%Y-%m-%dT%H:%M:%SZ', date: '$$evse.last_updated' } } }
]
},
{
$map: {
input: '$$evse.connectors',
as: 'connector',
in: {
$mergeObjects: [
'$$connector',
{ last_updated: { $dateToString: { format: '%Y-%m-%dT%H:%M:%SZ', date: '$$connector.last_updated' } } }
]
}
}
}
]
}
}
}
])
In this case the returned documents look like this:
[
{
"id" : "A",
"name" : "CHARGEPOINT WVCC 1684DELL 1",
"evses" : [
[
{
"uid" : "B",
"connectors" : [
{
"id" : "C",
"standard" : "IEC_62196_T1",
"last_updated" : ISODate("2021-02-18T23:54:56.000+0000")
}
],
"last_updated" : "2021-02-18T23:54:56Z"
},
[
{
"id" : "C",
"standard" : "IEC_62196_T1",
"last_updated" : "2021-02-18T23:54:56Z"
}
]
],
[
{
"uid" : "D",
"connectors" : [
{
"id" : "E",
"standard" : "IEC_62196_T1",
"last_updated" : ISODate("2021-02-19T22:15:43.000+0000")
}
],
"last_updated" : "2021-02-19T22:15:43Z"
},
[
{
"id" : "E",
"standard" : "IEC_62196_T1",
"last_updated" : "2021-02-19T22:15:43Z"
}
]
]
],
"last_updated" : "2021-12-14T23:42:06Z"
},
{
"id" : "F",
"name" : "5th Avenue",
"last_updated" : "2022-01-12T13:12:01Z",
evses: null
}
]
Is it possible to perform a $map within another $map without duplicating the evses.connectors array and nesting them in another array?
Your query should be as below:
Remove the [] from in for the $map.
Merge objects for $$evse, last_updated document and connectors document.
db.collection.aggregate([
{
$addFields: {
last_updated: {
$dateToString: {
format: "%Y-%m-%dT%H:%M:%SZ",
date: "$last_updated"
}
},
evses: {
$map: {
input: "$evses",
as: "evse",
in: {
$mergeObjects: [
"$$evse",
{
last_updated: {
$dateToString: {
format: "%Y-%m-%dT%H:%M:%SZ",
date: "$$evse.last_updated"
}
}
},
{
connectors: {
$map: {
input: "$$evse.connectors",
as: "connector",
in: {
$mergeObjects: [
"$$connector",
{
last_updated: {
$dateToString: {
format: "%Y-%m-%dT%H:%M:%SZ",
date: "$$connector.last_updated"
}
}
}
]
}
}
}
}
]
}
}
}
}
}
])
Sample Mongo Playground

Mongodb add an element to an array in specific order

I would like to push a new value to an array in a specific position but although I tried differents things I can do it. Can someone help me?
My document is:
{
"_id" : ObjectId("55528f000000000000000000"),
"contractId" : "55528f000000000000000000",
"field1": [
{
"name":"example",
"backendData": {
"map": {
"7552" : "RTEST",
"3511" : "TESTR",
"5312" : "JKTLE",
"5310" : "INVTS"
}
},
"data": {
"defaultOrder": [
"7552",
"3511",
"5312",
"5310"
]
}
}
]
}
I've tried with set, but I didn't find a way to add the element in the position I want, then I tried with push but it's not working. I did something like that:
db.collection.update({
"_id": ObjectId("55528f000000000000000000"),
},
{
"$push": {
"field1.$[i].data.$.defaultOrder": {
"$each": [
"9999"
],
"$position": 2
}
}
},
{
arrayFilters: [
{
"i.entities": {
$elemMatch: {
"name": "example"
}
}
}
]
})
And although it gives me a match it doesn't make any change
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
Expected outcome:
{
"_id" : ObjectId("55528f000000000000000000"),
"contractId" : "55528f000000000000000000",
"field1": [
{
"name":"example",
"backendData": {
"map": {
"7552" : "RTEST",
"3511" : "TESTR",
"5312" : "JKTLE",
"5310" : "INVTS"
}
},
"data": {
"defaultOrder": [
"7552",
"3511",
"9999",
"5312",
"5310"
]
}
}
]
}
Thank in advance
Please try this (edited both 2nd and 3rd parts):
db.collection.update({"_id": ObjectId("55528f000000000000000000")},
{"$push": {"field1.$[i].data.defaultOrder":
{"$each": ["9999"], "$position": 2}}},
{arrayFilters: [ { "i.name": "example" } ]})
See:
https://mongoplayground.net/p/8o57t4tlnm6

Group and Sum multi-dimensional array after unwinding elements

I have a multidimensional array below is my JSON data, I want the output mentioned below. Thanks for the help. I have tried two methods, 1. project with map there I am unable to group the data.
2. tried with unwind and group there unable to get the inside errorField array ("recordCount" : 73).
By using groupBy key needs to group by the results, if filed key matches & inside field--> detail key matches we need to merge those do document.
My JSON:
[
{
"_id":{
"$oid":"60f5c60fbc43cb00965ac641"
},
"groupBy":{
"$oid":"60f5c60fbc43cb00965ac641"
},
"employer":{
"$oid":"60d0e4001a6ccd764cb26df4"
},
"parameters":{
"begin":"2020-01-01",
"end":"2020-12-31"
},
"recordCount":74,
"errorFields":[
{
"field":"employeeStatus",
"recordCount":62,
"errorDetails":[
{
"recordIds":[
"10000986",
"10000990",
"10001020"
],
"detail":"Active employees should not have term date. Termed employees should have term date.",
"recordCount":3
},
{
"recordIds":[
"10001032"
],
"detail":"Stale pay period data (no new check for over 30 days from queried end date)",
"recordCount":1
}
]
},
{
"field":"ytdGrossWages.ytdTotalGrossWages",
"recordCount":8,
"errorDetails":[
{
"recordIds":[
"10001211",
"10001269",
"10001328",
"10001395"
],
"detail":"YTD total does not equal sum of YTD wage buckets",
"recordCount":4
}
]
}
],
"timestamp":{
"$date":"2021-07-19T18:35:59.031Z"
},
"__v":0
},
{
"_id":{
"$oid":"60f5c615bc43cb00965ac647"
},
"groupBy":{
"$oid":"60f5c60fbc43cb00965ac641"
},
"employer":{
"$oid":"60d0e4001a6ccd764cb26df4"
},
"parameters":{
"begin":"2020-01-01",
"end":"2020-12-31"
},
"recordCount":11,
"errorFields":[
{
"field":"employeeStatus",
"recordCount":11,
"errorDetails":[
{
"recordIds":[
"10003644",
"10003680"
],
"detail":"Active employees should not have term date. Termed employees should have term date.",
"recordCount":2
},
{
"recordIds":[
"10003667",
"10003694",
"10003807",
"10003789"
],
"detail":"Stale pay period data (no new check for over 30 days from queried end date)",
"recordCount":4
}
]
},
{
"field":"ssn",
"recordCount":2,
"errorDetails":[
{
"recordIds":[
"10003667"
],
"detail":"The ssn field is required.",
"recordCount":1
},
{
"recordIds":[
"10003694"
],
"detail":"The ssn must be 9 digits.",
"recordCount":1
}
]
},
{
"field":"employeeHomeAddressCountry",
"recordCount":1,
"errorDetails":[
{
"recordIds":[
"10003694"
],
"detail":"The employeeHomeAddressCountry field is required.",
"recordCount":1
}
]
}
],
"timestamp":{
"$date":"2021-07-19T18:36:05.135Z"
},
"__v":0
}
]
I want output like this:
{
"_id" : ObjectId("60f5c60fbc43cb00965ac641"),
"errorFields" : [
{
"field" : "employeeStatus",
"recordCount" : 73,
"errorDetails" : [
{
"recordIds" : [
"10001032",
"10003667",
"10003694",
"10003807",
"10003789"
],
"detail" : "Stale pay period data (no new check for over 30 days from queried end date)",
"recordCount" : 5
},
{
"recordIds" : [
"10000986",
"10000990",
"10001020",
"10001031",
"10001035"
],
"detail" : "Active employees should not have term date. Termed employees should have term date.",
"recordCount" : 5
}
]
},
{
"field" : "ytdGrossWages.ytdTotalGrossWages",
"recordCount" : 8,
"errorDetails" : [
{
"recordIds" : [
"10001211",
"10001269",
"10001328",
"10001395"
],
"detail" : "YTD total does not equal sum of YTD wage buckets",
"recordCount" : 8
}
]
},
{
"field" : "ssn",
"recordCount" : 2,
"errorDetails" : [
{
"recordIds" : [
"10003667"
],
"detail" : "The ssn field is required.",
"recordCount" : 1
},
{
"recordIds" : [
"10003694"
],
"detail" : "The ssn must be 9 digits.",
"recordCount" : 1
}
]
},
{
"field":"employeeHomeAddressCountry",
"recordCount":1,
"errorDetails":[
{
"recordIds":[
"10003694"
],
"detail":"The employeeHomeAddressCountry field is required.",
"recordCount":1
}
]
}
]
}
Here is the mycode method 1:
db.collection.aggregate([
{ $match: { groupBy: ObjectId("60f5c60fbc43cb00965ac641") } },
{ "$project": {
"_id": "$groupBy",
"errorFields": { "$map": {
"input": "$errorFields",
"as": "ef",
"in": {
"field": "$$ef.field",
"recordCount": {
$sum:"$$ef.recordCount"
},
"errorDetails": { "$map": {
"input": "$$ef.errorDetails",
"as": "ed",
"in": {
"detail": "$$ed.detail",
"recordIds": { "$map": {
"input": "$$ed.recordIds",
"as": "ri",
"in": {
$concat: [ "$$ri"]
}
}},
"recordCount": {
$size:"$$ed.recordIds"
}
}
}}
}
}}
}}
]).pretty()
Here is the mycode method 2:
db.collection.aggregate([
{ $match: { groupBy: ObjectId("60f5c60fbc43cb00965ac641") } },
{ $unwind: "$errorFields" },
{ $unwind: "$errorFields.errorDetails" },
{ $unwind: "$errorFields.errorDetails.recordIds" },
{ "$group": {
"_id": {
"_id": "$groupBy",
"errorFields": {
"field": "$errorFields.field",
"errorDetails": {
"detail": "$errorFields.errorDetails.detail"
}
}
},
"recordIds": {
"$push" : "$errorFields.errorDetails.recordIds",
},
"Idscount": { $sum: 1 }
}},
{ "$group": {
"_id": {
"_id": "$_id._id",
"errorFields": {
"field": "$_id.errorFields.field"
}
},
"errorDetails": {
"$push": {
"recordIds": "$recordIds",
"detail": "$_id.errorFields.errorDetails.detail",
"recordCount" : "$Idscount"
}
}
}},
{ "$group": {
"_id": 0,
"errorFields": {
"$push": {
"field": "$_id.errorFields.field",
"recordCount": "$fieldCount",
"errorDetails": "$errorDetails"
}
}
}}
]).pretty()

Mongo regex with array of elements not accurate

Document schema is as follows
"events": [
{
"title": "title1"
},
{
"title": "title2"
},
{
"title": "title3"
}
]
I have requirement to search (regex ) in events.title field and get the only matching element/object from the array . For that i am querying like this ,
db.collection.find({ "$or" : [ { "events.title" : { "$regex" : ".*title2.*" , "$options" : "i"}} , { "events.title" : { "$regex" : ".*title5.*" , "$options" : "i"}}] , "events" : { "$elemMatch" : { "title" : { "$ne" : null }}}},{"events.$":1,"_id":0});
I was expecting the result would be { "events" : [ { "title" : "title2"} ] } , but it returns
{ "events" : [ { "title" : "title1"} ] }
How can i update the query so that only matching element in array is returned in result ?
UPDATE
"events": {
$elemMatch: {
"$or": [{
"title": {
"$regex": ".*title2.*",
"$options": "i"
}
},
{
"title": {
"$regex": ".*title5.*",
"$options": "i"
}
}]
}
}
did the trick . Now it returns only matching element in the array irrespective of the position.
Thanks for the help
Try this
db.collection.find({events: {
$elemMatch: {
title: {$in: [/.*title2.*/i,
/.*title5.*/i]
}
}
}
},
{"events.$": 1, _id: 0});
{"events": {
$elemMatch: {
"$or": [{
"title": {
"$regex": ".*title2.*",
"$options": "i"
}
},
{
"title": {
"$regex": ".*title5.*",
"$options": "i"
}
}]
}
}
}
in projection did the trick . Now it returns only matching element in the array irrespective of the position

Resources