MongoDB create an array within an array - json

I am struggling to get my head around MongoDB and aggregates and groups. I've spent about 3 days so far.
I have source data that looks like...
{
"formName" : "my form",
"updatedAt" : "2021-11-02T13:29:00.123Z",
},
{
"formName" : "another form",
"lastUpdated" : "2021-10-01T13:29:00.123123",
},
Note that there are potentially different date names, though these are the only differences.
I am attempting to achieve an output of...
{
"_id": null,
"text": "my form", (NOTE: This is the formName)
"children": [{
"text" : 2021, (This is the year part of the updated)
"children" : [
{"text" : 1}, (These are the month part of the updated)
{"text" : 2},
{"text" : 3},
{"text" : 4}
]
},
]
}
So, basically a tree, which has formName, with child branch of years, with child branch of months.
I have tried all kinds of things, many don't work, such as nested $addToSet inside $groups.
I have been close, but I can't solve it.
This is the closest, but this doesn't work.
db.FormsStore.aggregate( [
{$match:{myKey:"a guid to group my forms together"}},
{$project: {formName:1, lastUpdated:1, updatedAt:1}},
{
$group: {
_id: { formName: "$formName" },
Year: {$addToSet: {$year: {$dateFromString: { dateString: "$lastUpdated" }}}},
Month: {$addToSet: {$month: {$dateFromString: { dateString: "$lastUpdated" }}}},
}
},
{
$group: {
_id: { formName: "$_id.formName" },
Time: {$addToSet: {year: "$Year", month: "$Month"}}
}
}
]
)
The output from that is showing...
{
_id: { formName: 'One of my forms' },
Time: [
{
year: [ 2021 ],
month: [ 10, 11 ]
}
]
}
This will all be used in C#
Your help would be greatly appreciated.

Query
adds a new field "date" with the date on Date format
group first the more specific (formname+year) to put the months in the array
group then the less specific (formname) to put the years in the array
aggregate(
[{"$set":
{"date":
{"$cond":
["$updatedAt", {"$dateFromString": {"dateString": "$updatedAt"}},
{"$dateFromString": {"dateString": "$lastUpdated"}}]},
"updatedAt": "$$REMOVE",
"lastUpdated": "$$REMOVE"}},
{"$group":
{"_id": {"text": "$formName", "year": {"$year": "$date"}},
"children": {"$push": {"text": {"$month": "$date"}}}}},
{"$group":
{"_id": "$_id.text",
"children":
{"$push": {"text": "$_id.year", "children": "$children"}}}},
{"$set": {"text": "$_id", "_id": "$$REMOVE"}}])
Edit
The bellow sorts also by year/month, and keeps only unique year/months per formName.
the difference is the group by formName,year,month to take the unique (first aacumulator will take one only of those that have the same in all 3)
replace-root (make that first document ROOT document)
and then sort by those 3 fields (descending year,ascending month)
group
sort by 2 fields
final group
PlayMongo
*mongoplaygroung loses the order of fields, run it on your driver also to be sure
aggregate(
[{"$set":
{"date":
{"$cond":
["$updatedAt", {"$dateFromString": {"dateString": "$updatedAt"}},
{"$dateFromString": {"dateString": "$lastUpdated"}}]},
"updatedAt": "$$REMOVE",
"lastUpdated": "$$REMOVE"}},
{"$set": {"year": {"$year": "$date"}, "month": {"$month": "$date"}}},
{"$group":
{"_id": {"formName": "$formName", "year": "$year", "month": "$month"},
"doc": {"$first": "$$ROOT"}}},
{"$replaceRoot": {"newRoot": "$doc"}},
{"$sort": {"formName": 1, "year": -1, "month": 1}},
{"$group":
{"_id": {"text": "$formName", "year": "$year"},
"children": {"$push": {"text": "$month"}}}},
{"$sort": {"_id.text": 1, "_id.year": -1}},
{"$group":
{"_id": "$_id.text",
"children":
{"$push": {"text": "$_id.year", "children": "$children"}}}},
{"$set": {"text": "$_id", "_id": "$$REMOVE"}}])
With data
[
{
"formName": "my form",
"updatedAt": "2021-11-02T23:30:15.123Z"
},
{
"formName": "my form",
"updatedAt": "2021-10-02T23:30:15.123Z"
},
{
"formName": "my form",
"updatedAt": "2020-06-02T23:30:15.123Z"
},
{
"formName": "my form",
"updatedAt": "2020-07-02T23:30:15.123Z"
},
{
"formName": "another form",
"updatedAt": "2021-10-01T23:30:15.123Z"
},
{
"formName": "another form",
"updatedAt": "2021-10-01T23:30:15.123Z"
},
{
"formName": "another form",
"updatedAt": "2021-09-01T23:30:15.123Z"
},
{
"formName": "another form",
"updatedAt": "2021-08-01T23:30:15.123Z"
},
{
"formName": "another form",
"updatedAt": "2020-10-01T23:30:15.123Z"
}
]
I got results
[{
"children": [
{
"text": 2021,
"children": [
{
"text": 10
},
{
"text": 11
}
]
},
{
"text": 2020,
"children": [
{
"text": 6
},
{
"text": 7
}
]
}
],
"text": "my form"
},
{
"children": [
{
"text": 2021,
"children": [
{
"text": 8
},
{
"text": 9
},
{
"text": 10
}
]
},
{
"text": 2020,
"children": [
{
"text": 10
}
]
}
],
"text": "another form"
}]

I continued to work on it, and while this is also not quite right (yet), here is what I came up with.
db.FormsStore.aggregate([
{$project: {formName:1, lastUpdated:1, updatedAt:1}},
{
$group: {
_id: {formName: "$formName", Year: {$year: {$dateFromString: { dateString: "$updatedAt" }}}, Month: {$month: {$dateFromString: { dateString: "$updatedAt" }}}},
}
},
{
$group: {
_id: {formName: "$_id.formName", Year: "$_id.Year"},
Months: {$addToSet: {Text: "$_id.Month"}}
}
},
{
$group: {
_id: "$_id.formName", Children: {$addToSet: {Text: "$_id.Year", Children: "$Months"}},
}
}
])
Getting all my data in the first group, then creating a set with the months in the second group, then creating a set with the years and adding the months to each year in the third group.

Related

INSERT JSON One by one

I get some problem with insert json at LOOP to Oracle DB.
{
"items": [
{
"identificationMethod": "N",
"idNumber": "7779",
"bonuses": [
{
"dateFrom": "2023-01-01",
"dateTo": "2023-12-31",
"value": 500,
"currency": "PLN",
"name": "11DOD",
"kind": "1"
},
{
"dateFrom": "2023-01-01",
"dateTo": "2023-12-31",
"value": 500,
"currency": "PLN",
"name": "22DOD",
"kind": "1"
}
]
},
{
"identificationMethod": "N",
"idNumber": "7790",
"bonuses": [
{
"dateFrom": "2023-01-01",
"dateTo": "2023-12-31",
"value": 500,
"currency": "PLN",
"name": "333DOD",
"kind": "1"
},
{
"dateFrom": "2023-01-01",
"dateTo": "2023-12-31",
"value": 500,
"currency": "PLN",
"name": "444DOD",
"kind": "1"
}
]
}
]
}
What i need to do is insert first Item with 2 bonuses, next second item with another bonuses
FOR IDOD IN ( SELECT
json.*
FROM
json_table(i_body,'$.items.bonuses[*]' COLUMNS( DATE1 PATH '$.dateFrom',
DATE2 PATH '$.dateTo',
value1 PATH '$.value',
value2 PATH '$.currency',
name PATH '$.name',
kind PATH '$.kind' )) json)
LOOP
END LOOP;
The case is to replace * in '$.items.bonuses[*]' with 0 next whit 1 next 2.... etc. HOW ?!
I try to use variables but nothing work.

Nested json - store values in csv

I am trying to convert a nested json file into csv. It's data from a darts API and the structure is always the same. Nevertheless I got some problems flattening and storing the values in a csv because of the nested structure.
json:
{
"summaries": [{
"sport_event": {
"id": "sr:sport_event:12967512",
"start_time": "2017-11-11T13:15:00+00:00",
"start_time_confirmed": true,
"sport_event_context": {
"sport": {
"id": "sr:sport:22",
"name": "Darts"
},
"category": {
"id": "sr:category:104",
"name": "International"
},
"competition": {
"id": "sr:competition:597",
"name": "Grand Slam of Darts"
},
"season": {
"id": "sr:season:47332",
"name": "Grand Slam of Darts 2017",
"start_date": "2017-11-11",
"end_date": "2017-11-20",
"year": "2017",
"competition_id": "sr:competition:597"
},
"stage": {
"order": 1,
"type": "league",
"phase": "stage_1",
"start_date": "2017-11-11",
"end_date": "2017-11-15",
"year": "2017"
},
"round": {
"number": 1
},
"groups": [{
"id": "sr:league:29766",
"name": "Grand Slam of Darts 2017, Group G",
"group_name": "G"
}]
},
"coverage": {
"live": true
},
"competitors": [{
"id": "sr:competitor:35936",
"name": "Smith, Michael",
"abbreviation": "SMI",
"qualifier": "home"
}, {
"id": "sr:competitor:83895",
"name": "Wilson, James",
"abbreviation": "WIL",
"qualifier": "away"
}]
},
"sport_event_status": {
"status": "closed",
"match_status": "ended",
"home_score": 5,
"away_score": 3,
"winner_id": "sr:competitor:35936"
}
}, {
"sport_event": {
"id": "sr:sport_event:12967508",
"start_time": "2017-11-11T13:40:00+00:00",
"start_time_confirmed": true,
"sport_event_context": {
"sport": {
"id": "sr:sport:22",
"name": "Darts"
},
"category": {
"id": "sr:category:104",
"name": "International"
},
"competition": {
"id": "sr:competition:597",
"name": "Grand Slam of Darts"
},
"season": {
"id": "sr:season:47332",
"name": "Grand Slam of Darts 2017",
"start_date": "2017-11-11",
"end_date": "2017-11-20",
"year": "2017",
"competition_id": "sr:competition:597"
},
"stage": {
"order": 1,
"type": "league",
"phase": "stage_1",
"start_date": "2017-11-11",
"end_date": "2017-11-15",
"year": "2017"
},
"round": {
"number": 1
},
"groups": [{
"id": "sr:league:29764",
"name": "Grand Slam of Darts 2017, Group F",
"group_name": "F"
}]
},
"coverage": {
"live": true
},
"competitors": [{
"id": "sr:competitor:70916",
"name": "Bunting, Stephen",
"abbreviation": "BUN",
"qualifier": "home"
}, {
"id": "sr:competitor:191262",
"name": "de Zwaan, Jeffrey",
"abbreviation": "DEZ",
"qualifier": "away"
}]
},
"sport_event_status": {
"status": "closed",
"match_status": "ended",
"home_score": 5,
"away_score": 4,
"winner_id": "sr:competitor:70916"
}
}
So for each sport_event I would like to store the variables:
"start_time"
from "season" the variable "name"
from "competitors" both "id" and "name"
from "sport_event_status" the "winner_id"
I have already tried to flatten the json file with this code:
import json
f = open(r'path of file.json')
data = json.load(f)
def flatten(data):
for key,value in data.items():
print (str(key)+'->'+str(value))
if type(value) == type(dict()):
flatten(value)
elif type(value) == type(list()):
for val in value:
if type(val) == type(str()):
pass
elif type(val) == type(list()):
pass
else:
flatten(val)
flatten(data)
print(data)
This actually prints out the following:
id->sr:season:47332
name->Grand Slam of Darts 2017
start_date->2017-11-11
end_date->2017-11-20
year->2017
competition_id->sr:competition:597
Now my question is how to store the values I mentioned above in a csv file.
Thanks in advance for your support.
Using jq, you basically just have to transcribe your specification, adding a bit of context and taking care of an embedded array:
.summaries[]
| .sport_event # Your specification:
| [.start_time, # start_time
.sport_event_context.season.name] # from "season" the variable "name"
+ [.competitors[] | .id, .name] # from "competitors" both "id" and "name"
+ [.sport_event_status.winner_id] # from "sport_event_status" the "winner_id"
| #csv
Invocation
E.g.
jq -rf program.jq my.json

Conditionally add a field in JSON transformation using JQ

I am trying to transform my JSON to different structure using JQ. I am able to achieve my new structure, however i am getting feilds with Null objects if they are not present in Source, my client wants to remove the fields if they are having null values..
As i iterate i am able to get the structure in new format. But additional structures are coming.
Code Snippet- https://jqplay.org/s/w2N_Ozg9Ag
JSON
{
"amazon": {
"activeitem": 2,
"createdDate": "2019-01-15T17:36:31.588Z",
"lastModifiedDate": "2019-01-15T17:36:31.588Z",
"user": "net",
"userType": "new",
"items": [
{
"id": 1,
"name": "harry potter",
"state": "sold",
"type": {
"branded": false,
"description": "artwork",
"contentLevel": "season"
}
},
{
"id": 2,
"name": "adidas shoes",
"state": null ,
"type": {
"branded": false,
"description": "Spprts",
"contentLevel": "season"
}
},
{
"id": 3,
"name": "watch",
"type": {
"branded": false,
"description": "walking",
"contentLevel": "special"
}
},
{
"id": 4,
"name": "adidas shoes",
"state": "in inventory",
"type": {
"branded": false,
"description": "running",
"contentLevel": "winter"
}
}
],
"product": {
"id": 4,
"name": "adidas shoes",
"source": "dealer",
"destination": "resident"
}
}
}
JQ Query:
.amazon | { userType: .userType, userName: .user, itemCatalog: (.items | map({ itemId: .id, name, state} )) }
Expected Response:
{
"userType": "new",
"userName": "net",
"itemCatalog": [
{
"itemId": 1,
"name": "harry potter",
"state": "sold"
},
{
"itemId": 2,
"name": "adidas shoes"
},
{
"itemId": 3,
"name": "watch"
},
{
"itemId": 4,
"name": "adidas shoes",
"state": "in inventory"
}
]
}
With the query i have, i am getting state : null for the entries which has empty or null values. I want to hide the field itself in these cases.
Horrible solution, delete them after the query. There must be a neater way? (I started using jq today)
.amazon | { userType: .userType, userName: .user, itemCatalog: (.items | map({ itemId: .id, name, state} )) }| del(.itemCatalog[].state |select(. == null))
https://jqplay.org/s/jlNYmJNi25

JSON Transformation using JQ

I am trying to transform my JSON to different structure using JQ. I am able to partially achieve my new structure, however i get a additional blocks of data.
As i iterate i am able to get the structure in new format. But additional structures are coming.
Code Snippet- https://jqplay.org/s/3gulSlZiWz
JSON
{
"amazon": {
"activeitem": 2,
"createdDate": "2019-01-15T17:36:31.588Z",
"lastModifiedDate": "2019-01-15T17:36:31.588Z",
"user": "net",
"userType": "new",
"items": [
{
"id": 1,
"name": "harry potter",
"state": "sold",
"type": {
"branded": false,
"description": "artwork",
"contentLevel": "season"
}
},
{
"id": 2,
"name": "adidas shoes",
"state": "in inventory",
"type": {
"branded": false,
"description": "Spprts",
"contentLevel": "season"
}
},
{
"id": 3,
"name": "watch",
"state": "returned",
"type": {
"branded": false,
"description": "walking",
"contentLevel": "special"
}
},
{
"id": 4,
"name": "adidas shoes",
"state": "in inventory",
"type": {
"branded": false,
"description": "running",
"contentLevel": "winter"
}
}
],
"product": {
"id": 4,
"name": "adidas shoes",
"source": "dealer",
"destination": "resident"
}
}
}
JQ Query:
.amazon|
{
userType: .userType,
userName: .user,
itemCatalog: {
itemId: .items[].id,
name: .items[].name
}
}
Expected Response:
{
"userType": "new",
"userName": "net",
"itemCatalog": {
"itemId": 1,
"name": "harry potter"
},{
"itemId": 2,
"name": "adidas shoes"
}, {
"itemId": 3,
"name": "watch"
},{
"itemId": 4,
"name": "adidas shoes"
}
}
But getting something weird long duplicated response.
As already pointed out in a comment, the "expected response" is not JSON and probably not what you want anyway. The following would make sense and in any case illustrates how to iterate appropriately:
.amazon
| { userType: .userType,
userName: .user,
itemCatalog: (.items | map({ itemId: .id, name} ))
}
Output
{
"userType": "new",
"userName": "net",
"itemCatalog": [
{
"itemId": 1,
"name": "harry potter"
},
{
"itemId": 2,
"name": "adidas shoes"
},
{
"itemId": 3,
"name": "watch"
},
{
"itemId": 4,
"name": "adidas shoes"
}
]
}

How to update the items on hand quantity with QuickBooks Online Plus

I am using QuickBooks Online Plus, which means i can edit the item. I would like to use the API to change the on hand # for an item. I've created an item "test1" through QuickBooks Online Plus. When i read the item from API i got the following in the attached json file. things looks great as follow:
{
"QueryResponse": {
"Item": [
{
"Name": "test1",
"Active": true,
"FullyQualifiedName": "test1",
"Taxable": false,
"UnitPrice": 3,
"Type": "Inventory",
"IncomeAccountRef": {
"value": "60",
"name": "Sales of Product Income"
},
"PurchaseCost": 1,
"ExpenseAccountRef": {
"value": "61",
"name": "Cost of Goods Sold"
},
"AssetAccountRef": {
"value": "62",
"name": "Inventory Asset"
},
"TrackQtyOnHand": true,
"QtyOnHand": 6,
"InvStartDate": "2015-12-02",
"domain": "QBO",
"sparse": false,
"Id": "19",
"SyncToken": "1",
"MetaData": {
"CreateTime": "2015-12-01T14:38:23-08:00",
"LastUpdatedTime": "2015-12-01T14:38:42-08:00"
}
}
],
"startPosition": 1,
"maxResults": 1
},
"time": "2015-12-02T09:59:29.936-08:00"
}
But when i tried to update that item using the json object below:
{
"Name": "test1",
"Active": true,
"Taxable": false,
"UnitPrice": 3,
"Type": "Inventory",
"IncomeAccountRef": {
"value": "60",
"name": "Sales of Product Income"
},
"PurchaseCost": 1,
"ExpenseAccountRef": {
"value": "61",
"name": "Cost of Goods Sold"
},
"AssetAccountRef": {
"value": "62",
"name": "Inventory Asset"
},
"TrackQtyOnHand": true,
"QtyOnHand": 16,
"InvStartDate": "2015-12-02",
"domain": "QBO",
"sparse": false,
"Id": "19",
"SyncToken": "2"
}
, i got an error like this:
{"Fault":{"Error":[{"Message":"Stale Object Error","Detail":"Stale Object Error : You and seven.li#hotschedules.com were working on this at the same time. seven.li#hotschedules.com finished before you did, so your work was not saved.","code":"5010","element":""}],"type":"ValidationFault"},"time":"2015-12-02T10:42:52.466-08:00"}
I would like to update the on hand quantity. Does anyone know what's wrong and how to do it? Thanks.
The problem is with the "SyncToken". I believe when you try to update the new item, you should not increment the "SyncToken", you should keep it the same. QBO will update the "SyncToken" for you.