quickest way of filtering a list in jinja2 - jinja2

Give the data below for a payload/list of pay items,
{
"details":{
"totalPay":482.66,
"currency":"OMR",
"contributions":48.24,
"sections":[
{
"name":"Earnings",
"value":519.41,
"details":[
{
"name":"Overtime",
"value":60,
"notes":"Border OT",
"currency":"OMR"
},
{
"name":"H R A",
"value":96,
"notes":"",
"currency":"OMR"
},
{
"name":"LIVING ALLOWANCE",
"value":32,
"notes":"",
"currency":"OMR"
},
{
"name":"T A",
"value":32,
"notes":"",
"currency":"OMR"
},
{
"name":"Basic salary",
"value":299.408,
"notes":"",
"currency":"OMR"
}
]
},
{
"name":"Deductions",
"value":36.75,
"details":[
{
"name":"PASI - Employee (7%) - Employee",
"value":32.1586,
"notes":null,
"currency":"OMR"
},
{
"name":"Social Security Recovery (1%) - Employee",
"value":4.5941,
"notes":null,
"currency":"OMR"
}
]
}
]
}
}
I would like get values for specific names. For e.g I would like to get the value for Basic salary which is 299.488
My script stops working when i use the below,
{% set basicSalary = (data.details.sections.details | selectattr('name', 'eq', 'Basic salary')| (attribute='value')%}
What would be the shortest and quickest way of doing this ?

Related

Removing a specific attribute in an array of nested documents

Excuse my English, I'm from Russia.
I asked this question in the Russian version SO, but they still haven't answered it.
There is a record collection that stores archival files. Here is its simplified structure (I omitted most of the attributes):
{
"_id": 1,
"tomes": [
{
"number":1,
"archive_number":1
},
{
"number":2,
"archive_number":1
}
]
}
{
"_id": 2,
"tomes": [
{
"number":1,
"archive_number":1
},
{
"number":2,
"archive_number":1
},
{
"number":3,
"archive_number":1
}
]
}
I need to remove the archive_number attribute from each of the nested documents of the tomes array for all documents in the record collection.
After deletion, the structure should look like this:
{
"_id": 1,
"tomes": [
{
"number":1,
},
{
"number":2,
}
]
}
{
"_id": 2,
"tomes": [
{
"number":1,
},
{
"number":2,
},
{
"number":3,
}
]
}
I was able to write a query like this:
db.record.update(
{
"tomes": {
$elemMatch:{
"archive_number":{$exists:true}
}
}
},
{
$unset: {
"tomes.$.archive_number":1
}
},
false, true
)
But this query only removes the archive_number attribute on one volume per archive case. I.e., after launch, we will see the following picture:
{
"_id": 1,
"tomes": [
{
"number":1,
},
{
"number":2,
"archive_number":1
}
]
}
{
"_id": 2,
"tomes": [
{
"number":1,
},
{
"number":2,
"archive_number":1
},
{
"number":3,
"archive_number":1
}
]
}
Can you please tell me how to delete all volumes? I don’t know how to correct the request, but my head doesn’t understand anymore.
Solution 1
With $[<indentifier>] (filtered positional operator) and arrayFilters to update the document(s) in the array.
db.collection.update({
"tomes": {
$elemMatch: {
"archive_number": {
$exists: true
}
}
}
},
{
$unset: {
"tomes.$[tome].archive_number": 1
}
},
{
arrayFilters: [
{
"tome.archive_number": {
$exists: true
}
}
],
multi: true
})
Sample Mongo Playground (Solution 1)
Solution 2
With $[] (all positional operator).
The all positional operator $[] indicates that the update operator should modify all elements in the specified array field.
db.collection.update({
"tomes": {
$elemMatch: {
"archive_number": {
$exists: true
}
}
}
},
{
$unset: {
"tomes.$[].archive_number": 1
}
},
{
multi: true
})
Sample Mongo Playground (Solution 2)
References
How the arrayFilters Parameter Works in MongoDB

Find the average value in MongoDB from JSON

In my MongoDB (export from JSON file) I have database "dab" with structure like this:
id:"1"
datetime:"2020-05-08 5:09:56"
name:"namea"
lat:55.826738
lon:45.0423412
analysis:"[{"0":0.36965591924860347},{"5":0.10391287134268598},{"10":0.086884394..."
I'm using that db for spark analysis via MongoDB-Spark Connector.
My problem is field "analysis" - I need average result for all values from every interval ("0", "5", "10", ..., "1000"), so I have to sum 0.36965591924860347 + 0.10391287134268598 + 0.086884394 + ... and divide by number of intervals (I have 200 intervals in every column), and finally multiply the result by 100.
My solution would be this one:
db.collection.aggregate([
{
$set: {
analysis: {
$map: {
input: "$analysis",
in: { $objectToArray: "$$this" }
}
}
}
},
{
$set: {
analysis: {
$map: {
input: "$analysis",
in: { $first: "$$this.v" }
}
}
}
},
{ $set: { average: { $multiply: [ { $avg: "$analysis" }, 100 ] } } }
])
Mongo playground
You can use $reduce on that array,sum the values,and then divide with the number of elements and then multiply with 100
db.collection.aggregate([
{
"$addFields": {
"average": {
"$multiply": [
{
"$divide": [
{
"$reduce": {
"input": "$analysis",
"initialValue": 0,
"in": {
"$let": {
"vars": {
"sum": "$$value",
"data": "$$this"
},
"in": {
"$add": [
"$$sum",
{
"$arrayElemAt": [
{
"$arrayElemAt": [
{
"$map": {
"input": {
"$objectToArray": "$$data"
},
"as": "m",
"in": [
"$$m.k",
"$$m.v"
]
}
},
0
]
},
1
]
}
]
}
}
}
}
},
{
"$size": "$analysis"
}
]
},
100
]
}
}
}
])
You can test the code here
But this code has 1 problem, you save data in documents, and MongoDB
doesn't have a function like get(document,$$k), the new MongoDB v5.0 has a $getField but still accepts only constants no variables.
I mean we cant do in your case getField(doc,"5").
So we have the cost of converting each document to an array.

Any smart JSON/JS Object counting packages?

I don't mean to be duplicating any questions, since I have read so many great answers here on StackOverflow.
Given the below JSON data:
[
{
“department”: “vice”,
“team”: [
{
"selected": "Yes"
},
{
“selected": “No”
}
],
“fund”: [
“team_a”
“team_c”
]
}
]
I want to return a count of selected, so from the above 'yes'=1 and 'no'=1.
I understand I can do it through for loop, using a simple countYes++ to return the answer, however I have 2 questions as below:
Is there any other way (i.e. using some npm package).
In my example the options are simple Yes and No, if I have a bigger list of things to count, how to count all unique ones > 0?
Thanks.
I tried to write using vanilla JS you can look for library like underscore, lodash for more options.
var data = [
{
"department": "vice",
"team": [
{
"selected": "Yes"
},
{
"selected": "No"
}
],
"fund": [
"team_a",
"team_c"
]
},
{
"department": "vice",
"team": [
{
"selected": "Yes"
},
{
"selected": "Yes"
}
],
"fund": [
"team_a",
"team_c"
]
},
{
"department": "vice",
"team": [
{
"selected": "Yes"
},
{
"selected": "No"
}
],
"fund": [
"team_a",
"team_c"
]
}
];
function decisionReducer(prev, next) {
var team = next.team;
if (team && Array.isArray(team)) {
team.forEach(function (row) {
if (row.selected === 'Yes') {
prev.yes += 1;
}
if (row.selected === 'No') {
prev.no += 1;
}
})
}
return prev;
};
var counter = data.reduce(decisionReducer, {yes: 0, no: 0})
console.log(counter);

Three-Way Relationship in Firebase

I've been learning a lot about denormalised data over the past few months, but I wanted to know if the following is possible in a flattened architecture world. I know how to handle two-way relationships in Firebase, but what about a three-way relationship. Let me explain...
I have 5 items in my database, services, providers, serviceAtProvider, reviews and users. I want to be able to add providers to services and vice versa.
I'd also like there to be a specific page for a provider inside a service, and for there to be reviews linked to the provider at that specific service. The page url might look like this (site.com/serviceId/providerId). The rating is unique to that providerId inside of that serviceId – you can't rate serviceIds or providerIds separately.
I'm not sure how to go about creating such a complex relationship. How would I join the serviceId and providerId in that serviceAtProvider item?
This is what I've got so far:
"services": {
"service1": {
"name": "Hernia Repair",
"providers": {
"provider1": true,
"provider2": true
}
}
},
"providers": {
"provider1": { "name": "The Whittington Hospital" },
"provider2": { "name": "Homerton Hospital" }
},
"serviceAtProvider": {
"service1AtProvider1": { "rating": 4 },
"service1AtProvider2": { "rating": 3 }
},
"reviews": {
"service1AtProvider1": {
"review1": {
"body": "A review from user 1",
"user": "user1"
}
},
"service1AtProvider2": {
"review1": {
"body": "A review from user 2",
"user": "user2"
}
}
},
"users": {
"user1": { "name": "Ben Thomas" },
"user2": { "name": "Beatrix Potter" }
}
I don't know how to create that serviceAtProviderjoin, or how would I go about accessing the service1.name, provider1.name, service1AtProvider1.rating, reviews.service1AtProvider1 on one page. Can anyone explain how to do this?
Also, are there any best practices I should follow?
Any help is appreciated. Thanks in advance!
UPDATE
{
"availableServices": {
"service1": { "name": "Hernia Repair" },
"service2": { "name": "Chemotherapy" }
},
"services": {
"provider": {
"name": "The Whittington Hospital",
"service": {
"service1": {
"rating": 4,
"reviewId1": true
},
"service2": {
"rating": 3,
"reviewId2": true
},
}
}
},
"reviews": {
"reviewId1": {
"review1": {
"rating": 4,
"body": "A review from user 1",
"user": "user1"
}
}
},
"users": {
"user1": { "name": "Raphael Essoo-Snowdon" },
"user2": { "name": "Sharlyne Slassi" }
}
}
I would start by making the data structure a bit simpler and more direct. It's hard to determine the correct data structure for your needs without a detailed use case. I'll do my best to make some generic assumptions here. You'll have to adapt as necessary.
{
"service": {
"service1": { "name": "Foo Service" },
...
},
"provider": {
"provider1": { name: "Foo Place" },
...
},
"ratings": {
"service1": { // service id
"provider1": { // provider id
"average_rating": 4
},
...
},
...
},
"reviews": {
"service1": { // service id
"provider1": { // provider id
"user": "user1",
"rating": 4
},
...
},
...
},
"user": {
"user1": { "name": "Foo Bar" },
...
}
}
Now, to look up the providers who offer a given service, and grab their reviews, I would do the following:
var ref = new Firebase(...);
ref.child('ratings/service1').on('child_added', function(reviewSnap) {
console.log(
'Provider ' + reviewSnap.key(),
'Average rating ' + reviewSnap.val().average_rating
);
});
Joining in the names of the services and providers could be done in several ways. Here's a manual technique:
var ref = new Firebase(...);
ref.child('ratings/service1').on('child_added', accumulateReview);
function accumulateReview(reviewSnap) {
var reviewData = reviewSnap.val();
var reviewid = reviewSnap.key();
fetchService(reviewSnap.parent().key(), function(serviceSnap) {
loadRec('provider', reviewSnap.key(), function(providerSnap) {
console.log('Provider: ', providerSnap.key(), providerSnap.val().name);
console.log('Service: ', serviceSnap.key(), serviceSnap.val().name);
console.log('Average rating: ', reviewData.average_rating);
});
});
}
var serviceCache = {};
function fetchService(serviceid, done) {
// demonstrates creating a local cache for things that will be
// looked up frequently
if( !serviceCache.hasOwnProperty(serviceid) ) {
cacheService(serviceid, done);
}
else {
done(serviceCache[serviceid]);
}
}
function cacheService(serviceid, done) {
loadRec('service', function(ss) {
serviceCache[serviceid] = ss;
fetchService(serviceid, done);
});
}
function loadRec(type, key, done) {
ref.child(type).child(key).once('value', done);
}
I could also automate some of this process with Firebase.util's NormalizedCollection:
var ref = new Firebase(...);
var nc = Firebase.util.NormalizedCollection(
[ref.child('reviews/service1'), 'review'],
ref.child('provider'),
ref.child('user')
)
.select('review.rating', {key: 'provider.name', alias: 'providerName'}, {key: 'user.name', alias: 'userName'})
.ref();
nc.on('child_added', function(snap) {
var data = snap.val();
console.log('Provider', data.providerName);
console.log('User', data.userName);
console.log('Rating', data.rating);
});
Note that nothing here is set in stone. This is how I would approach it. There are probably dozens of structures at least as good or better.

Mongodb insert with multiple conditions

I'm having multiple documents in a collection, each document has this data structure :
{
_id: "some object id",
data1: [
{
data2_id : 13233,
data2: [
{
sub_data1: "text1",
sub_data2: "text2",
sub_data3: "text3",
},
{
sub_data1: "text4",
sub_data2: "text5",
sub_data3: "text6",
}
]
},
{
data2_id : 53233,
data2: [
{
sub_data1: "text4",
sub_data2: "text5",
sub_data3: "text6",
}
...
]
},
{
data2_id : 56233,
data2: [
{
sub_data1: "text7",
sub_data2: "text8",
sub_data3: "text9",
}
...
]
},
{
data2_id : 53236,
data2: [
{
sub_data1: "text10",
sub_data2: "text22",
sub_data3: "text33",
}
...
]
}
]
}
I'd like to update to a set of ids that maches some condition, update only the sub object within the document.
I've tries this:
db.collection.update({
"$and": [
{
"_id": {
"$in": [
{
"$id": "54369aca9bc25af3ca8b4568"
},
{
"$id": "54369aca9bc25af3ca8b4562"
}
]
}
},
{
"data1.data2": {
"$elemMatch": {
"sub_data1": "text4",
"sub_data2": "text5"
}
}
}
]
},
{
"data1.data2.$.sub_data3" : "text updated"
}
)
But I get the following error:
Update of data into MongoDB failed: dev.**.com:27017: cannot use the part (data2 of data1.data2.0.sub_data3) to traverse the element...
Any Ideas?
There is an open issue here that imposes a limitation when trying to update elements of an array nested within another array.
Besides, there are some improvements you can do here:
For your query you don't need the $and
db.collection.update(
{
"_id": {
"$in": [
{"$id": "54369aca9bc25af3ca8b4568"},
{"$id": "54369aca9bc25af3ca8b4562"}
]},
"data1.data2": {
"$elemMatch": {
"sub_data1": "text4",
"sub_data2": "text5"
}
},{..update...})
You might want to use $set:
db.collection.update(query,{ $set:{"name": "Mike"} })
Otherwise, you might lose the rest of the data within your document.