Sorting by integer column doesn't sort by amount. Is it possible? - gridjs

My data:
data: [
["A","2"],
["B","100"],
]
I was expecting it to sort by amount: 2 then 100, but it's the opposite. First row shows 100, second row shows 2. Is there a way to sort by the actual amount rather than this numeric ordering?

As "2" and "100" are interpreted as string - the sort order is alphanumeric instead of numeric.
Try
data: [
["A",2],
["B",100],
]
instead to sort by the numbers.

new gridjs.Grid({
columns: [
"Fecha",
{
name: "Cantidad",
sort: {
compare: (a, b) => {
if (parseFloat(a) > parseFloat(b)) {
return 1;
} else if (parseFloat(b) > parseFloat(a)) {
return -1;
} else {
return 0;
}
},
},
},
"Tipo",
"Origen",
]

Related

How to do fast query on String dataType in MongoDB, where values are of type double

I have a field contractValue and other fields in a collection contract which is of type String . It basically holds double value like 1200 or 1500 but at some places it may contain value like $1200 or $1500.
Sample data from collection:
{ ..
..
contractValue: "1200", //This is the one stored as String. I need
// to perform range query over it
..
..
}
{ ..
..
contractValue: "$1500",
..
..
}
I have requirement where i need to fetch contracts based on contract values. Query can be like below:
{$and: [ {'contractValue': {$gt: 100}}, {'contractValue': {$lt: 1000 }}]}
This query is giving me wrong result. It is also giving me documents having contractValue like 1238999
Also I need to create indexes on contractValue
Is it possible to create index on contract value , so that I can efficiently make range query, so that whenever making any query, it will do < or > on Index and will fetch exact set of documents, rather than making change in schema?
How to handle values like $1200 in index, so index value just contain 1200 as integer
rather than $1200
try this:
https://mongoplayground.net/p/TG3Y5tdh9aK
it assumes string data will be either a quoted number or a quoted number with "$" at the front
db.collection.aggregate([
{
$project: {
"newContractValue": {
"$convert": {
"input": "$contractValue",
"to": "double",
"onError": {
$toDouble: {
"$substr": [
"$contractValue",
1,
{
"$strLenCP": "$contractValue"
}
]
}
}
}
}
}
},
{
$match: {
$and: [
{
"newContractValue": {
$gt: 100
}
},
{
"newContractValue": {
$lt: 1000
}
}
]
}
}
])
This can be used to set a new contractValueNew field as number from the existing contractValue
db.getCollection('yourCollection').find({})
.forEach(function(record) {
if(record.contractValue.toString().substring(0, 1) == '$') {
record.contractValueNew = NumberInt(parseInt(record.contractValue.substring(1, record.contractValue.length)));
} else {
record.contractValueNew = NumberInt(parseInt(record.contractValue))
}
db.getCollection('yourCollection').save(record)
})
Try:
db.collection.find({'contractValue': {$gt: 100, $lt: 1000 }})
Create index on contractValue , but convert all values as numbers ...

How to compare two arrays using $elemmatch in mongodb?

I need to compare two arrays to find whether all elements in the first array matches the second one.
First array:
var tasktime = [2,3,4];
Second array:
'working_days': [
{
'slots': [ 8, 9, 14, 15 ]
}
];
I need to check whether all the elements in the "tasktime" array exists in the "slots" array.
Below is the query I have tried but not getting the expected results.
var defaultCondition = [
{
query: {
"working_days": { $elemMatch: { slots: { $setIntersection: [ 'slots', tasktime ] } } }
}
}
];
db.GetAggregation('tasker', defaultCondition, function (err, taskers) {
if (err || !taskers[0]) {
res.send({ count: 0, result: [] });
} else {
callback(err, taskers);
}
});
Need someone's valuable help on this.
You need to use $all to find all the values in taskTime array.
You can simply do :
db.tasker.find({"working_days.slots" : {$all : taskTime}});

How to group by uniform intervals of data between a maximum and minimum using the MongoDB aggregator?

Let's say I have a whole mess of data that yields a range of integer values for a particular field... I'd like to see those ranked by a grouping of intervals of occurrence, perhaps because I am clustering...like so:
[{
_id: {
response_time: "3-4"
},
count: 234,
countries: ['US', 'Canada', 'UK']
}, {
_id: {
response_time: "4-5"
},
count: 452,
countries: ['US', 'Canada', 'UK', 'Poland']
}, ...
}]
How can I write a quick and dirty way to A) group the collection data by equally spaced intervals over B) a minimum and maximum range using a MongoDB aggregator?
Well, in order to quickly formulate a conditional grouping syntax for MongoDB aggregators, we first adopt the pattern, per MongoDB syntax:
$cond: [
{ <conditional> }, // test the conditional
<truthy_value>, // assign if true
$cond: [ // evaluate if false
{ <conditional> },
<truthy_value>,
... // and so forth
]
]
In order to do that muy rapidamente, without having to write every last interval out in a deeply nested conditional, we can use this handy recursive algorithm (that you import in your shell script or node.js script of course):
$condIntervalBuilder = function (field, interval, min, max) {
if (min < max - 1) {
var cond = [
{ '$and': [{ $gt:[field, min] }, { $lte: [field, min + interval] }] },
[min, '-', (min + interval)].join('')
];
if ((min + interval) > max) {
cond.push(ag.$condIntervalBuilder(field, (max - min), min, max));
} else {
min += interval;
cond.push(ag.$condIntervalBuilder(field, interval, min, max));
}
} else if (min >= max - 1 ) {
var cond = [
{ $gt: [field, max] },
[ max, '<' ].join(''), // Accounts for all outside the range
[ min, '<' ].join('') // Lesser upper bound
];
}
return { $cond: cond };
};
Then, we can invoke it in-line or assign it to a variable that we use elsewhere in our analysis.

How to use max operator in MongoDB

I want to know how to use the $max operator for the following problem:
Suppose this is the data given:
{
"_id" : ,
"attributes":
{
"value1":10,
"value2":50,
"value3":70,
"value4":25,
"value5":50,
"value6":20
}
}
I have provided one set of JSON. There are multiple JSON.
I want to find the maximum value for value3 = 70 in this case and get a "_id" corresponding to it which can be passed in next query as a parameter.
I have tried it but i am unable to get a correct answer.
db.collection.group(
{"_id": $null , {"attribute.value1":true},
reduce: function(obj,prev) {
if (prev.maxValue < obj."attribute.value") {
prev.maxValue = obj."attribute.value";
}
},
initial: { maxValue: 0 }}
);

Generating a Structure for Aggregation

So here's a question. What I want to do is generate a data structure given a set of input values.
Since this is a multiple language submission, let's consider the input list to be an array of key/value pairs. And therefore an array of Hash, Map, Dictionary or whatever term that floats your boat. I'll keep all the notation here as JSON, hoping that's universal enough to translate / decode.
So for input, let's say we have this:
[ { "4": 10 }, { "7": 9 }, { "90": 7 }, { "1": 8 } ]
Maybe a little redundant, but lets stick with that.
So from that input, I want to get to this structure. I'm giving a whole structure, but the important part is what gets returned for the value under "weight":
[
{ "$project": {
"user_id": 1,
"content": 1,
"date": 1,
"weight": { "$cond": [
{ "$eq": ["$user_id": 4] },
10,
{ "$cond": [
{ "$eq": ["$user_id": 7] },
9,
{ "$cond": [
{ "$eq": ["$user_id": 90] },
7,
{ "$cond": [
{ "$eq": ["$user_id": 1] },
8,
0
]}
]}
]}
]}
}}
]
So the solution I'm looking for populates the structure content for "weight" as shown in the structure by using the input as shown.
Yes the values that look like numbers in the structure must be numbers and not strings, so whatever the language implementation, the JSON encoded version must look exactly the same.
Alternately, give me a better approach to get to the same result of assigning the weight values based on the matching user_id.
Does anyone have an approach to this?
Would be happy with any language implementation as I think it is fair to just see how the structure can be created.
I'll try to add myself, but kudos goes to the good implementations.
Happy coding.
When I had a moment to think about this, I ran back home to perl and worked this out:
use Modern::Perl;
use Moose::Autobox;
use JSON;
my $encoder = JSON->new->pretty;
my $input = [ { 4 => 10 }, { 7 => 9 }, { 90 => 7 }, { 1 => 8 } ];
my $stack = [];
foreach my $item ( reverse #{$input} ) {
while ( my ( $key, $value ) = each %{$item} ) {
my $rec = {
'$cond' => [
{ '$eq' => [ '$user_id', int($key) ] },
$value
]
};
if ( $stack->length == 0 ) {
$rec->{'$cond'}->push( 0 );
} else {
my $last = $stack->pop;
$rec->{'$cond'}->push( $last );
}
$stack->push( $rec );
}
}
say $encoder->encode( $stack->[0] );
So the process was blindingly simple.
Go through each item in the array and get the key and value for the entry
Create a new "document" that has in array argument to the "$cond" key just two of required three entries. These are the values assigned to test the "$user_id" and the returned "weight" value.
Test the length of the outside variable for stack, and if it was empty (first time through) then push the value of 0 as seen in the last nested element to the end of the "$cond" key in the document.
If there was something already there (length > 0) then take that value and push it as the third value in the "$cond" key for the document.
Put that document back as the value of stack and repeat for the next item
So there are a few things in the listing such as reversing the order of the input, which isn't required but produces a natural order in the nested output. Also, my choice for that outside "stack" was an array because the test operators seemed simple. But it really is just a singular value that keeps getting re-used, augmented and replaced.
Also the JSON printing is just there to show the output. All that is really wanted is the resulting value of stack to be merged into the structure.
Then I converted the logic to ruby, as was the language used by the OP from where I got the inspiration for how to generate this nested structure:
require 'json'
input = [ { 4 => 10 }, { 7 => 9 }, { 90 => 7 }, { 1 => 8 } ]
stack = []
input.reverse_each {|item|
item.each {|key,value|
rec = {
'$cond' => [
{ '$eq' => [ '$user_id', key ] },
value
]
}
if ( stack.length == 0 )
rec['$cond'].push( 0 )
else
last = stack.pop
rec['$cond'].push( last )
end
stack.push( rec )
}
}
puts JSON.pretty_generate(stack[0])
And then eventually into the final form to generate the pipeline that the OP wanted:
require 'json'
userWeights = [ { 4 => 10 }, { 7 => 9 }, { 90 => 7}, { 1 => 8 } ]
stack = []
userWeights.reverse_each {|item|
item.each {|key,value|
rec = {
'$cond' => [
{ '$eq' => [ '$user_id', key ] },
value
]
}
if ( stack.length == 0 )
rec['$cond'].push( 0 )
else
last = stack.pop
rec['$cond'].push( last )
end
stack.push( rec )
}
}
pipeline = [
{ '$project' => {
'user_id' => 1,
'content' => 1,
'date' => 1,
'weight' => stack[0]
}},
{ '$sort' => { 'weight' => -1, 'date' => -1 } }
]
puts JSON.pretty_generate( pipeline )
So that was a way to generate a structure to be passed into aggregate in order to apply "weights" that are specific to a user_id and sort the results in the collection.
First thank you Neil for your help with this here, this workout great for me and it's really fast. For those who use mongoid, this is what I used to create the weight parameter where recommended_user_ids is an array:
def self.project_recommended_weight recommended_user_ids
return {} unless recommended_user_ids.present?
{:weight => create_weight_statement(recommended_user_ids.reverse)}
end
def self.create_weight_statement recommended_user_ids, index=0
return 0 if index == recommended_user_ids.count
{"$cond" => [{ "$eq" => ["$user_id", recommended_user_ids[index]] },index+1,create_weight_statement(recommended_user_ids,index+1)]}
end
So to add this to the pipeline simply merge the hash like this:
{"$project" => {:id => 1,:posted_at => 1}.merge(project_recommended_weight(options[:recommended_user_ids]))}