I'm using this query to grab the usage percentage of stickers (pdo):
SELECT
id_sticker,
CAST((COUNT(*) / :stickers_count * 100) AS UNSIGNED) as percentage
FROM user_sticker AS sticker_total
WHERE id_user_to = :id_user
GROUP BY id_sticker
ORDER BY percentage DESC
This is the final result:
{
"data": [
{
"id_sticker": 2,
"percentage": 28.5714285714
},
{
"id_sticker": 1,
"percentage": 14.2857142857
},
{
"id_sticker": 3,
"percentage": 14.2857142857
},
{
"id_sticker": 5,
"percentage": 14.2857142857
},
{
"id_sticker": 6,
"percentage": 14.2857142857
},
{
"id_sticker": 7,
"percentage": 14.2857142857
}
]
}
The total sum of the percentages is 99.9999999999 ... it should be 100 (that is triggering an error with the piechart component i'm using). Any ideas? Thanks!
SOLUTION
I ended adding this php fix after grabbing the data:
$dif = 100;
foreach($result as $item) {
$dif = $dif - $item['percentage'];
}
if($dif > 0) {
$result[0]['percentage'] += $dif;
} elseif($dif < 0) {
$result[count($result)-1]['percentage'] += $dif;
}
It's just a rounding error. If you need it to add up to 100, just round the values to 1 or 2 decimal places (that should be enough for a pie chart) and recalculate the last one as 100 - sum(1..(n-1)) (that's pseudocode, by the way).
You can't seriously be expecting perfect precision with floating arithmetic, can you?
Related
"response": {
"numFound": 2,
"start": 0,
"docs": [
{
"total_amountA": 10,
"total_amountB": 5,
"id": "2"
},
{
"total_amountA": 10,
"total_amountB": 5,
"id": "1"
}
]
}
i want to get the sum of all tatal_amount.
like this -> total_amountA : 20, total_amountB : 10
how can i get the sum of all value..? plese help me
The Stats Component gives you a set of statistics for a given field for a given query.
http://localhost:8983/solr/corename/select?q=your:query&stats=true&stats.field=total_amount
Should give you an additional stats key in the response with sum among the values calculated.
I know that common table expressions (CTE) a.k.a. "temporary named result sets" can be used in SQL to generate a temporary table, but can this be done in MongoDB? I want a document, but it's only for temporary use in my query.
Can you create a temporary table in MongoDB without creating a new collection?
For example, if I were to try to recreate the code below in Mongo...
Example CTE Table in SQL:
n
f1
f2
1
20
12
2
40
0.632
3
60
0.647
WITH RECURSIVE example (n, f1, f2) AS
( SELECT 1, 20, 12
UNION ALL SELECT
n + 1,
n * 20,
least(6*n, $globalVar * 100),
FROM example WHERE n < 3
) SELECT * FROM example
It seems that there is no general equivalent for CTE in MongoDB. However, for OP's example, it is possible to wrangle the output of $range to produce a similar effect.
// whichever collection doesn't matter; as long as it has 1 document then it should be fine
db.collection.aggregate([
{
// jsut take 1 document
"$limit": 1
},
{
// use $range to generate iterator [1, 2, 3]
"$addFields": {
"rg": {
"$range": [
1,
4
]
},
globalVar: 0.001
}
},
{
// do the mapping according to logic
"$addFields": {
"cte": {
"$map": {
"input": "$rg",
"as": "n",
"in": {
n: "$$n",
f1: {
"$multiply": [
"$$n",
20
]
},
f2: {
"$cond": {
"if": {
$lt: [
{
"$multiply": [
"$$n",
6
]
},
{
"$multiply": [
"$globalVar",
100
]
}
]
},
"then": {
"$multiply": [
"$$n",
6
]
},
"else": {
"$multiply": [
"$globalVar",
100
]
}
}
}
}
}
}
}
},
{
// wrangle back to expected form
"$unwind": "$cte"
},
{
"$replaceRoot": {
"newRoot": "$cte"
}
}
])
Here is the Mongo playground for your reference.
I have a nested JSON structure stored in a PostgreSQL table.
Table users:
id | content [JSON]
JSON:
{
"purchases": [
{
"id": 1,
"costs": [
{
"amount": 23
},
{
"amount": 34
}
]
},
{
"id": 2,
"costs": [
{
"amount": 42
}
]
}
]
}
I would like to add a field "jsonClass": "Static" to all the objects within the costs array so I have following in the end:
{
"purchases": [
{
"id": 1,
"costs": [
{
"jsonClass": "Static",
"amount": 23
},
{
"jsonClass": "Static",
"amount": 34
}
]
},
{
"id": 2,
"costs": [
{
"jsonClass": "Static",
"amount": 42
}
]
}
]
}
I couldn't figure out how to add values to such a nested structure. Anyone knows how to achieve such thing? The only way I found was to make it a text and do string replace which is not very performant and I have a lot of such entries.
Unfortunately, due to having to change multiple sub-objects, I don't know of a better way than to deconstruct and then reconstruct the object. It gets pretty hairy.
UPDATE users
SET content=(
SELECT jsonb_agg(purchase)
FROM (
SELECT jsonb_build_object('id', pid, 'purchases', jsonb_agg(cost)) AS purchase
FROM (
SELECT pid, cost || '{"jsonClass":"static"}'::jsonb AS cost
FROM (
SELECT purchase->'id' AS pid, jsonb_array_elements(purchase->'costs') AS cost
FROM jsonb_array_elements(content::jsonb->'purchases') AS purchase
) AS Q
) AS R
GROUP BY pid
) AS S
);
Fiddle
EDIT: Sorry about all the edits, forgot to test for multiple rows. Should be good now. It might be possible to simplify it a bit more, not sure.
I am new to MongoDB and I am trying to turn SQL queries into MongoDB queries. But can't seem to find any way to turn a SQL query with a subquery to mongoDB.
for example:
SELECT article, dealer, price
FROM shop
WHERE price=(SELECT MAX(price) FROM shop);
I tried the following, but it doesn't seem to work.
db.shop.group({
"initial": {},
"reduce": function(obj, prev) {
prev.maximumvalueprice = isNaN(prev.maximumvalueprice) ? obj.price :
Math.max(prev.maximumvalueprice, obj.price);
}}).forEach(
function(data){
db.shop.find({
"price": data
},
{
"article": 1,
"dealer": 1,
"price": 1
})
})
How do I convert this SQL query into a MongoDB query?
If you are using MongoDB v. 3.2 or newer you can try to use $lookup.
Try to use aggregation:
$sort your collection by price by DESC;
set $limit to 1 (it will take a first document, which will be with biggest price);
then use $lookup to select the documents from the same collection by max price and set it to tmpCollection element;
$unwind tmpCollection;
$replaceRoot - change document root to $tmpCollection
Example:
db.getCollection("shop").aggregate([
{$sort: {"price":-1}},
{$limit: 1},
{$lookup: {
from: "shop",
localField: "price",
foreignField: "price",
as: "tmpCollection"
}},
{$unwind: "$tmpCollection"},
{$replaceRoot: {newRoot:"$tmpCollection"}}
]);
Looks like you need the aggregation framework for this task using $first within a $group pipeline stage on ordered documents. The initial pipeline step for ordering the documents in the collection is $sort:
db.shop.aggregate([
{ "$sort": { "price": -1 } }, // <-- sort the documents first in descending order
{
"$group": {
"_id": null,
"article": { "$first": "$article" },
"dealer": { "$first": "$dealer" },
"price": { "$first": "$price" }
}
}
])
or using $last
db.shop.aggregate([
{ "$sort": { "price": 1 } }, // <-- note the sort direction
{
"$group": {
"_id": null,
"article": { "$last": "$article" },
"dealer": { "$last": "$dealer" },
"price": { "$last": "$price" }
}
}
])
I have the below requirement.
In the below array elements I have to select and compare the value of LoanAmount. In the previous posts, the below solutions are mentioned.
{
"_id": "65c5e4c917781f7365f4d814f6e1665f",
"_rev": "2-73615006996721fef9507c2d1dacd184",
"userprofile": {
"name": "tom",
"age": 30,
"employer": "Microsoft"
},
"loansBorrowed": [{"loanamount": 5000,
"loandate": "01/01/2001",
"repaymentdate": "01/01/2001",
"rateofinterest": 5.6,
"activeStatus": true,
"penalty": {
"penalty-amount": 500,
"reasonforPenalty": "Exceeded the date by 10 days"
}
},
{
"loanamount": 3000,
"loandate": "01/01/2001",
"repaymentdate": "01/01/2001",
"rateofinterest": 5.6,
"activeStatus": true,
"penalty": {
"penalty-amount": 400,
"reasonforPenalty": "Exceeded the date by 10 days"
}
},
{
"loanamount": 2000,
"loandate": "01/01/2001",
"repaymentdate": "01/01/2001",
"rateofinterest": 5.6,
"activeStatus": true,
"penalty": {
"penalty-amount": 500,
"reasonforPenalty": "Exceeded the date by 10 days"
}
}
]
}
Index:
{
"index": {
"fields": [{
"name": "loansBorrowed.[].loanamount",
"type":"number"
}],
"type": "json"
}
Selector query:
{"selector": {
"loansBorrowed": {
"$elemMatch": {
"loanamount": 3000
}
}
}
}
But that index and selector queries are providing all the records for that particular Query instead of providing me only record with 3000.
Please suggest how to fetch only particular element inside an array block.
I don't think it's possible to only return specific items in an array. You could accomplish something similar using views. Here is an example design document:
{
"_id": "_design/loans",
"_rev": "1-a115abe01632dd43ee1d0d10546b737d",
"views": {
"by_amount": {
"map": "function (doc) {\n if (doc.loansBorrowed) {\n for (var i=0; i<doc.loansBorrowed.length; i++) {\n emit(doc.loansBorrowed[i].loanamount, {userprofile: doc.userprofile, loan:doc.loansBorrowed[i]});\n }\n }\n}"
}
},
"language": "javascript"
}
This creates a view called by_amount. Here is the map function:
function (doc) {
if (doc.loansBorrowed) {
for (var i=0; i<doc.loansBorrowed.length; i++) {
emit(doc.loansBorrowed[i].loanamount, {userprofile: doc.userprofile, loan:doc.loansBorrowed[i]});
}
}
}
Here I am using the loan amount as the key. This let's you query by the loan amount. The value can be whatever you want to return. In this case I am returning a document with the user's profile and the loan.
You can then query this view like so:
https://xxx.cloudant.com/YOUR_DB/_design/loans/_view/by_amount?key=3000
Which results in the something like the following (note: I added a second loan with a value of 3000 to show how it would look with multiple loans that matched):
{
"total_rows":6,
"offset":2,
"rows":[
{
"id":"796a8954600cee9dbb9e0a4040593942",
"key":3000,
"value":{
"userprofile":{
"name":"tom",
"age":30,
"employer":"Microsoft"
},
"loan":{
"loanamount":3000,
"loandate":"01/01/2001",
"repaymentdate":"01/01/2001",
"rateofinterest":5.6,
"activeStatus":true,
"penalty":{
"penalty-amount":400,
"reasonforPenalty":"Exceeded the date by 10 days"
}
}
}
},
{
"id":"c93f52da36a51f0ddd75f5be381c916e",
"key":3000,
"value":{
"userprofile":{
"name":"joe",
"age":50,
"employer":"Google"
},
"loan":{
"loanamount":3000,
"loandate":"01/01/2001",
"repaymentdate":"01/01/2001",
"rateofinterest":5.6,
"activeStatus":true,
"penalty":{
"penalty-amount":400,
"reasonforPenalty":"Exceeded the date by 10 days"
}
}
}
}
]
}