MongoDB update query for nest array - node.js

Having collection Measurement such as shown below:
{
"Data" : [ [-5, [[1, 1023.0], [2, 694.0]]], [-1, [[1, 0.0], [2, 20.0]]], [-3, [[1, 30.75], [2, 30.75]]] ]
}
it reflects c# structure of Dictionary<int, Dictionary<int, double>> - what I'd need to do is to write an update script which will add 5 to all the parental dictionary keys. How could this be done via mongo update script? So it would turn the object to look as follows:
{
"Data" : [ [0, [[1, 1023.0], [2, 694.0]]], [4, [[1, 0.0], [2, 20.0]]], [2, [[1, 30.75], [2, 30.75]]] ]
}

The only way to do this is programatically, i.e., looping over the Data array and updating each individually.

This is probably not the structure that you really want if you need to update things in this way. The problem lies with the ability to match elements in a nested array in that the current limitation is that you can only match the first position and reference that index only when doing an update.
We can't tell much about your purpose based on what you have presented, but what you probably need is something like this:
{
"Data" : [
{
"pos": 0,
"ref": -5,
"A": { "x": 1, "y": 1023.0 },
"B": { "x": 2, "y": 694.0 }
},
{
"pos": 1,
"ref": -1,
"A": { "x": 1, "y": 0.0},
"B": { "x": 2, "y": 20.0 }
},
{
"pos": 2,
"ref": -3,
"A": { "x": 1, "y": 30.75 },
"B": { "x": 2, "y": 30.75 }
}
]
}
Yet even that does not allow you to update in a single query. You can do it with one for each element though:
db.collection.update({"_id": id, "Data.pos": 0}, {"$inc":{"Data.$.ref": 5}});
db.collection.update({"_id": id, "Data.pos": 1}, {"$inc":{"Data.$.ref": 5}});
db.collection.update({"_id": id, "Data.pos": 3}, {"$inc":{"Data.$.ref": 5}});
And your current schema would not allow you to do even that. And at least all of the elements could be accessed in this way, which again they could not before.
In any case, updating all of the array elements at once is not possible other than in a loop:
db.collection.find({ "_id": id }).forEach(function(doc) {
doc.Data.forEach(function(data) {
data.ref += 5;
});
db.collection.update(
{ "_id": doc._id },
{ "$set": { "Data": doc.Data } }
);
})
Or some variant that might even do something like the first example rather that just replacing the whole array as this does. Your current structure would rely on looping through several nested arrays to do the same thing.
Of course if you regularly have to update all elements in this way, then consider something other than an array. Or live with how you have to update, according to what your data access needs are.
Read the documentation on how things can be handled and make you decisions from there.

Related

How to sort descending order with an Object result in NodeJs

I am using the below function to get number of duplicated values in an array.But i want to get this result sorted descending order with respect to the values.
function countRequirementIds() {
const counts = {};
const sampleArray = RIDS;
sampleArray.forEach(function(x) { counts[x] = (counts[x] || 0) + 1; });
console.log(typeof counts); //object
return counts
}
Output:
{
"1": 4,
"2": 5,
"4": 1,
"13": 4
}
required output:
{
"2": 5,
"1": 4,
"13": 4,
"4": 1,
}
Javascript object keys are unordered as explained here: Does JavaScript guarantee object property order?
So sorting objects by keys is impossible. However if order is of a matter for you I would suggest using array of tuples:
const arrayOfTuples = [
[ "1", 4],
[ "2", 5],
[ "4", 1],
[ "13", 4],
]
arrayOfTuples.sort((a,b) => b[1] - a[1]);
console.log(arrayOfTuples);
// => [ [ '2', 5 ], [ '1', 4 ], [ '13', 4 ], [ '4', 1 ] ]
The sort command. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort Arrays of objects can be sorted by comparing the value of one of their properties.

Polygon contains polygon [MONGODB]

My issue is the following. I have a collection (territories) that have a geometry of type Polygon inside. The thing is that the polygons in my applications must be validated against this collection. Meaning that for a polygon to be valid, it must be COMPLETELY contained in at least one territory.
As $geoWithin is intented to work only in one direction. I try to do the following.
db.territories.insertMany([
{
_id: 1,
geometry: {
type: "Polygon",
coordinates: [
[
[0, 0],
[5, 0],
[5, 5],
[0, 5],
[0, 0]
]
]
}
}
])
const aggregation = [
{
$addFields: {
polygonToValidate: {
type: "Polygon",
coordinates: [[[2, 2], [4, 2], [4, 4], [2, 4], [2, 2]]]
},
territoryPolygon: "$geometry",
},
},
{
$match: {
polygonToValidate: {
$geoWithin: {
$geometry: "$territoryPolygon"
}
}
}
}
]
db.territories.aggregate(aggregation);
The polygon is valid as it is completely contained inside the territory polygon. But is not posible to reference the document field, neither the field added in the $addFields (territoryPolygon).

Substring filtering in Altair / using "params"

I am using Altair and would like to filter data using a substring search. Here is an example of doing it in vega-lite. Here is the code:
{
"config": {"view": {"continuousWidth": 400, "continuousHeight": 300}},
"data": {"name": "d"},
"mark": "point",
"encoding": {
"x": {"type": "quantitative", "field": "xval", "scale":{"domain": [0,4]}},
"y": {"type": "quantitative", "field": "yval", "scale":{"domain": [1,10]}}
},
"params": [{"name": "Letter", "value": "A",
"bind": {"input": "select", "options": ["A", "B", "C", "D", "E", "F"]}
}],
"transform": [
{"filter": "indexof(datum.info, Letter)>-1"}
],
"datasets": {
"d": [
{"xval": 1, "yval": 7, "info": "A;B;D;E"},
{"xval": 2, "yval": 2, "info": "A;C;E;F"},
{"xval": 3, "yval": 9, "info": "A;B;D"}
]
}
}
This allows me to filter out rows that contain "A", "B", "C" etc. in the info column, but it relies on "params" which is not available in Altair yet - is there any other way of achieving this kind of "substring" filtering in Altair as of now? This is meant to be a minimal example, but I have a large number of "options" (many gene names) in my actual use case, so adding a column for each to the original data wouldn't be feasible.
Trying to do this in Altair because it is for an executable research article which I believe allows Altair but not vega-lite.
Edit: realized that indexing like infoSel.info[0] gives the string of the selection from the dropdown. This still worked with infoSel.info (with no index) but that was just lucky - in expressions like this doing infoSel.info[0] is more correct.
Got it! This is possible with an expression in transform_filter, which I had previously tried but done incorrectly (I was using the name of the dropdown, not the name of the select object):
d = pd.DataFrame({'xval': [1, 2, 3],
'yval': [7, 2, 9],
'info': ['A;B;D;E', 'A;C;E;F', 'B;D']})
info_dropdown = alt.binding_select(options=['A', 'B', 'C', 'D', 'E', 'F'], name='Letter')
info_sel = alt.selection_single(name='infoSel', fields=['info'], bind=info_dropdown, init={'info': 'A'})
alt.Chart(d).mark_circle().encode(
x='xval', y='yval'
).add_selection(info_sel).transform_filter('indexof(datum.info, infoSel.info[0])>-1')

Mongoose js - pushAll, concat arrays

I need to update (in bulk) many entities.
Each entity has a field that its value is an array.
I want to concat a whole array to the existed array in mongo.
For example:
Assume we have the field 'myField', and (its value) the array saved in mongo is: [4, 5, 6]
I want to concat the array [1, 2, 3] to this field, so the result:
myField: [1, 2, 3, 4, 5, 6]
I tried some options:
pushAll - but it is no longer available.
usePushEach: true, in options - not working, still get the same error:
"Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array"
I read about concat - but it is not looks compatiable.
Thanks in advance!
you can use $addToSet to add the values to existing array and avoid the duplicates like so,
[
{
id: 1,
values: [
1,
2,
3
]
}
]
db.collection.update({
id: 1
},
{
"$addToSet": {
values: {
"$each": [
5,
7,
1,
44
]
}
}
})
https://mongoplayground.net/p/S3HfWajg9r_

Spatial Indexes with DocumentDB

I’m trying to do a spatial query against DocumentDB that looks like this:
SELECT * FROM root r WHERE
ST_WITHIN({'type':'Point','coordinates':[-122.02625, 37.4718]}, r.boundingBox)
to match a document that looks like this in the collection:
{
"userId": "747941cfb829",
"id": "747941cfb829_1453640096710",
"boundingBox": {
"type": "Polygon",
"coordinates": [
[-122.0263, 37.9718],
[-122.0262, 37.9718],
[-122.0262, 36.9718],
[-122.0263, 36.9718],
[-122.0263, 37.9718]
]
},
"distance": 0,
"duration": 1
}
I’ve turned on spatial indexes ala https://azure.microsoft.com/en-us/documentation/articles/documentdb-geospatial/ but I’m not getting a match back from DocumentDB.
Any ideas?
NOTE: Corrected GeoJson coordinate order.
The correct specification of a GeoJSON polygon has an additional array around the coordinates than you show to allow for the possibility of holes and multipolygons. So, it would look like this:
{
"type": "Polygon",
"coordinates": [
[
[0, 0], [10, 10], [10, 0], [0, 0]
]
]
}

Resources