RethinkDB Node.js - Subtract values from each field in a document, using another object - rethinkdb

Given a table which contains objects that look something like this:
{
someString: 'something',
foo: 100,
bar: 53,
baz: 230,
ignore: 'everything but numbers'
}
and a given JSON object that contains only number values:
{
foo: 20,
bar: 15,
baz: 100
}
I need a ReQL query to subtract all the values in the given JSON object from the values in a selected document. However, the catch is that I don't know what fields are in the given JSON object, it could contain one, all, or none of the number fields (it will never contain non-number fields, or fields that are not in the original table at all).
I understand that I could easily just get the document in question and subtract the values myself and update the document with the new values, but these values are constantly changing. So if I do that then there may be a chance that the values could change in between getting the document and updating it, causing incorrect values to be inserted. So I would like to do the actual subtraction in a query.

r.db("db").table("table").get("id")
.update({
"foo":r.row("foo").sub(20),
"bar":r.row("bar").sub(15),
"baz":r.row("baz").sub(100)
})
You can then modify this update in your code based on what values you have in your given JSON object.

Related

Can't get Power Automate Filter Array to render distinct result

I've got 2 Arrays. One of the Arrays is a list of Office 365 Contacts. The other Array contains a list of Customers from an API. I've used a Select statement on my Contacts Array, so that it is in the same format as my Customer Array. When I create the following Filter Array, I never get the distinct item I'm looking for.
Here is the Code when I use Peek Code.
{
"inputs": {
"from": "#variables('Customers Arrary')",
"where": "#not(contains(variables('Contacts Array'), item()))"
},
"metadata": {
"operationMetadataId": "9ab3697e-6f5c-41b4-b94e-41641ce3dacf"
}
}
The result I'm looking for is to find all the items in the Customer Array that don't match to anything in the Contacts Array. The unique identifiers are email address and mobile number.
Any help is much appreciated! Thanks in advance!
I finally got this to work. In order for this to work, the data sets have to match. For example, if you are comparing two data sets against an email address, that's the only data you can have, and you better not have any null values. Here is an example filter I created. The JSON from the two arrays only contains email address values.
So, make sure and remove any data that does not appear in both data sets. I would also remove any items that contain null values.

Year over Year Stats from a Crossfilter Dataset

Summary
I want to pull out Year over Year stats in a Crossfilter-DC driven dashboard
Year over Year (YoY) Definition
2017 YoY is the total units in 2017 divided by the total units in 2016.
Details
I'm using DC.js (and therefore D3.js & Crossfilter) to create an interactive Dashboard that can also be used to change the data it's rendering.
I have data, that though wider (has ~6 other attributes in addition to date and quantity: size, color, etc...sales data), boils down to objects like:
[
{ date: 2017-12-7, quantity: 56, color: blue ...},
{ date: 2017-2-17, quantity: 104, color: red ...},
{ date: 2016-12-7, quantity: 60, color: red ...},
{ date: 2016-4-15, quantity: 6, color: blue ...},
{ date: 2017-2-17, quantity: 10, color: green ...},
{ date: 2016-12-7, quantity: 12, color: green ...}
...
]
I'm displaying one rowchart per attribuet such that you can see the totals by color, size, etc. People would use each of these charts to be able to see the totals by that attribute and drill into the data by filtering by just a color, or a color and a size, or a size, etc. This setup is all (relatively) straight forward and kind of what DC is made for.
However, now I'd like to add some YoY stats such that I can show a barchart with x-axis as the years, and the y-axis as the YoY values (ex. YoY-2019 = Units-2019 / Units-2018). I'd also like to do the same by quarter and month such that I could see YoY Mar-2019 = Units-Mar-2019 / Units-Mar-2018 (and the same for quarter).
I have a year dimension and sum quantity
var yearDim = crossfilterObject.dimension(_ => _.date.getFullYear());
var quantityGroup = yearDim.group.reduceSum(_ => _.quantity);
I can't figure out how to do the Year over Year calc though in the nice, beautiful DC.js-way.
Attempted Solutions
Year+1
Add another dimension that's year + 1. I didn't' really get any further though because all I get out of it are two dimensions whose year groups I want to divide ... but am not sure how.
var yearPlusOneDim = crossfilterObject.dimension(_ => _.date.getFullYear() + 1);
Visually I can graph the two separately and I know, conceptually, what I want to do: which is divide the 2017 number in yearDim by the 2017 number in YearPlusOneDim (which, in reality, is the 2016 number). But "as a concept is as far as I got on this one.
Abandon DC Graphing
I could always use the yearDim's quantity group to get the array of values, which I could then feed into a normal D3.js graph.
var annualValues = quantityGroup.all();
console.log(annualValues);
// output = [{key: 2016, value: 78}, {key: 2017, value: 170}]
// example data from the limited rows listed above
But this feels like a hacky solution that's bound to fail and not benefit from all the rapid and dynamic DC updating.
I'd use a fake group, in order to solve this in one pass.
As #Ethan says, you could also use a value accessor, but then you'd have to look up the previous year each time a value is accessed - so you'd probably have to keep an extra table around. With a fake group, you only need this table in the body of your .all() function.
Here's a quick sketch of what the fake group might look like:
function yoy_group(group) {
return {
all: function() {
// index all values by date
var bydate = group.all().reduce(function(p, kv) {
p[kv.key.getTime()] = kv.value;
return p;
}, {});
// for any key/value pair which had a value one year earlier,
// produce a new pair with the ratio between this year and last
return group.all().reduce(function(p, kv) {
var date = d3.timeYear.offset(kv.key, -1);
if(bydate[date.getTime()])
p.push({key: kv.key, value: kv.value / bydate[date.getTime()]});
return p;
}, []);
}
};
}
The idea is simple: first index all the values by date. Then when producing the array of key/value pairs, look each one up to see if it had a value one year earlier. If so, push a pair to the result (otherwise drop it).
This should work for any date-keyed group where the dates have been rounded.
Note the use of Array.reduce in a couple of places. This is the spiritual ancestor of crossfilter's group.reduce - it takes a function which has the same signature as the reduce-add function, and an initial value (not a function) and produces a single value. Instead of reacting to changes like the crossfilter one does, it just loops over the array once. It's useful when you want to produce an object from an array, or produce an array of different size from the original.
Also, when indexing an object by a date, I use Date.getTime() to fetch the numeric representation of the date. Otherwise the date coerces to a string representation which may not be exact. Probably for this application it would be okay to skip .getTime() but I'm in the habit of always comparing dates exactly.
Demo fiddle of YOY trade volume in the data set used by the stock example on the main dc.js page.
I've rewritten #Gordon 's code below. All the credit is his for the solution (answered above) and I've just wirtten down my own version (far longer and likely only useful for beginners like me) of the code (much more verbose!) and the explanation (also much more verbose) to replicate my thinking in bridging my near-nothing starting point up to #Gordon 's really clever answer.
yoyGroup = function(group) {
return { all: function() {
// For every key-value pair in the group, iterate across it, indexing it by it's time-value
var valuesByDate = group.all().reduce(function(outputArray, thisKeyValuePair) {
outputArray[thisKeyValuePair.key.getTime()] = thisKeyValuePair.value;
return outputArray;
}, []);
return group.all().reduce(function(newAllArray, thisKeyValuePair) {
var dateLastYear = d3.timeYear.offset(thisKeyValuePair.key, -1);
if (valuesByDate[dateLastYear.getTime()]) {
newAllArray.push({
key: thisKeyValuePair.key,
value: thisKeyValuePair.value / valuesByDate[dateLastYear.getTime()] - 1
});
}
return newAllArray;
}, []); // closing reduce() and a function(...)
}}; // closing the return object & a function
};
¿Why are we overwritting the all() function?
When DC.js goes to create a graph based on a grouping, the only function from Crossfilter it uses is the all() function. So if we want to do something custom to a grouping to affect a DC graph, we only have to overwrite that one function: all().
¿What does the all() function need to return?
A group's all function must return an array of objects and each object must have two properties: key & value.
¿So what exactly are we doing here?
We're starting with an existing group which shows some values over time (Important Assumption: keys are date objects) and then creating a wrapper around it so that we can take advantage of the work that crossfilter has already done to aggregate at a certain level (ex. year, month, etc.).
We start by using reduce to manipulate the array of objects into a more simple array where the keys and values that were in the objects are now directly in the array. We do this to make it easier to look up values by keys.
before / output structure of group.all()
[ {key: k1, value: v1},
{key: k2, value: v2},
{key: k3, value: v3}
]
after
[ k1: v1,
k2: v2,
k3: v3
]
Then we move on to creating the correct all() structure again: an array of objects each of which has a key & value property. We start with the existing group's all() array (once again), but this time we have the advantage of our valuesByDate array which will make it easy to look up other dates.
So we iterate (via reduce) over the original group.all() output and lookup in the array we generated earlier (valuesByDate), if there's an entry from one year ago (valuesByDate[dateLastYear.getTime()]). (We use getTime() so it's simple integers rather than objects we're indexing off of.) If there is an element of the array from one year ago, then we add a key-value object-pair to our soon-to-be-returned array with the current key (date) and for the value we divide the "now" value (thisKeyValuePair.value) by the value 1 year ago: valuesByDate[dateLastYear.getTime()]. Lastly we subtract 1 so that it's (the most traditional definition of) YoY. Ex. This year = 110 and last year = 100 ... YoY = +10% = 110/100 - 1.

Errors when grouping by list in Power Query

I have a set of unique items (Index) to each of which are associated various elements of another set of items (in this case, dates).
In real life, if a date is associated with an index, an item associated with that index appeared in a file generated on that date. For combination of dates that actually occurs, I want to know which accounts were present.
let
Source = Table.FromRecords({
[Idx = 0, Dates = {#date(2016,1,1), #date(2016,1,2), #date(2016,1,3)}],
[Idx = 1, Dates = {#date(2016,2,1), #date(2016,2,2), #date(2016,2,3)}],
[Idx = 2, Dates = {#date(2016,1,1), #date(2016,1,2), #date(2016,1,3)}]},
type table [Idx = number, Dates = {date}]),
// Group by
Grouped = Table.Group(Source, {"Dates"}, {{"Idx", each List.Combine({[Idx]}), type {number}}}),
// Clicking on the item in the top left corner generates this code:
Navigation = Grouped{[Dates={...}]}[Dates],
// Which returns this error: "Expression.Error: Value was not specified"
// My own code to reference the same value returns {0,2} as expected.
CorrectValue = Grouped{0}[Idx],
// If I re-make the table as below the above error does not occur.
ReMakeTable = Table.FromColumns(Table.ToColumns(Grouped), Table.ColumnNames(Grouped))
in ReMakeTable
It seems that I can use the results of this in my later work even without the Re-make (I just can't preview cells correctly), but I'd like to know if what's going on that causes the error and the odd code at the Navigation step, and why it disappears after the ReMakeTable step.
This happens because when you double click an item, the auto-generated code uses value filter instead of row index that you are using to get the single row from the table. And since you have a list as a value, it should be used instead of {...}. Probably UI isn't capable to work with lists in such a situation, and it inserts {...}, and this is indeed an incorrect value.
Thus, this line of code should look like:
Navigate = Grouped{[Dates = {#date(2016,1,1), #date(2016,1,2), #date(2016,1,3)}]}[Idx],
Then it will use value filter.
This is a bug in the UI. The index the UI calculates is incorrect: it should be 0 instead of [Dates={...}]. ... is a placeholder value, and it generates the "Value was not specified" exception if it is not replaced.

How to create x-axis range groups with Crossfilter and dc.js?

Here is a fiddle to help show what I'd like to do:
http://jsfiddle.net/m4x7o5of/
I have a set of records, each with a float value. For example:
var records = [{name: 'record1', value: 1.34563}, ..., {name: 'record5000', value: 0.62974}];
I'd like to create a barchart in dc.js that plots the records on the x-axis in range buckets, e.g x-number of records with value between 0 and .5, y-number of records between .5 and 1, z-number of records between 1 and 1.5, and so on.
I'm using an ordinal scale so that I can divide the set of records into 5ths, but I can't figure out how to get the records grouped together in the ranges like I described. In the linked fiddle, only the records with a value that matches the plotted ordinals will get displayed right now.
Is it even possible to group the records like this? Any help would be appreciated.
dimension.group takes a function that you can use to derive the group key. So dimension.group(function(d) { return Math.floor(d); }); will give you group keys of 0, 1, 2, 3, 4, 5, 6, 7, and 8 for your data set. You'll just need to construct a function that returns the values you want based on the values in your data set. Is that what you're looking to do?

Indexing coordinates in MongoDB not working

I have a model that stores coordinates and the coordinates are indexed
class Place
include Mongoid::Document
include Mongoid::Spacial::Document
field :coordinates, type: Array, spacial: true
index({ coordinates: "2d" }, { unique: true })
end
However, whenever I save a place with a set of coordinates then create another, both coordinates are saved. This leads me to believe that the indexing of coordinates are not working. What am I missing here and how can I fix it?
An example below.
place = Place.new(coordinates: [50, 50])
place.save # returns true in console
place2 = Place.new(coordinates: [50, 50])
place2.save # returns true in console
# Thus I have two place records with the same exact coordinates, something I don't want
The 2d and 2dsphere index types don't enforce the unique constraint at all. I've created a DOCS issue to clarify this in the documentation: https://jira.mongodb.org/browse/DOCS-1701

Resources