Mongoose js - pushAll, concat arrays - node.js

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_

Related

MongoDB: auto sort documents in ascending order

I have a list of documents in a database collection. The documents ordered get changed by a front-end drag-drop functionality. order field of a document sort all documents in ascending order. For example,
[{
order: 1,
no: 1
......
},
{
order: 2,
no: 2
......
},
{
order: 3,
no: 3
......
}]
Now. when drag-drop is applied it may change to different formats. For example -
[{
order: 3,
no: 1
......
},
{
order: 1,
no: 2
......
},
{
order: 2,
no: 3
......
}]
Now it is not an issue to sort the list from the front end. Later by clicking a button, a new record is added to the top of the list. It's not a problem at all to sort again to the ascending order from the client side.
Actual Issue
I have pagination on the page so every time I don't have all items to sort from the client-side programming. There should have only/best way to sort them from the MongoDB query. For example, using the following query I can sort documents -
db.collection.aggregate([
{
"$sort": {
order: 1
}
}
])
But there is a problem if order field has duplicate numbers or doesn't have any number the above query can't sort it. Is there any query to sort the order field automatically in ascending order regardless have duplicate numbers or no value at all?
For example, if we have documents in the following format -
[
{
order: null,
no: 1,
...
},
{
order: 1,
no: 2,
...
},
{
order: 1,
no: 3,
...
},
{
order: 3,
no: 4,
...
},
]
it should be converted to -
[
{
order: 1,
no: 1,
...
},
{
order: 2,
no: 2,
...
},
{
order: 3,
no: 3,
...
},
{
order: 4,
no: 4,
...
},
]
We can use $addFields and $ifNull to set a default value of nullable field on query
db.collection.aggregate([
{
$addFields: {
order: { $ifNull: ["$order", "$order", -1] }, // order will be -1 if it is null
},
}
])
.sort({ order: 1 });

Mongoose/Node: selecting element WITHOUT field/column name

So I have document like this
datatable: [{
data:[
["ABC", 123, 10, 1],
["ABC", 121, 10, 1],
["DDE", 13, 10, 1],
["OPP", 523, 10, 1]
]
}]
I want to select with a parameter "ABC" and would return arrays only with "ABC" like this:
datatable: [{
data:[
["ABC", 123, 10, 1],
["ABC", 121, 10, 1]
]
}]
Im starting with this code:
router.get("/", (req, res) => {
model.find({}).then(val=> {
res.send(val)
})
})
I cant find ways to find the value without the fieldname.
I tried using $elemMatch. Other ways needs a matching column name with the value.

JsonPath - Restrict $..id Operator To Avoid Traversing Object Hierarchy

I was trying to extract data from a JSON object using jsonpath package for following JSON structure -
[
{
"id": 1,
"images": [
{ "id": 1,"url": "http://url1.jpg" },
{ "id": 2,"url": "http://url2.jpg" }
]
},
{
"id": 2,
"images": [
{ "id": 3,"url": "http://url3.jpg" },
{ "id": 4,"url": "http://url4.jpg" }
]
},
{
"id": 3,
"images": [
{ "id": 5,"url": "http://url5.jpg" },
{ "id": 6,"url": "http://url6.jpg" }
]
}
]
In he above example, $..id json-path expression responds with following array -
[ 1, 1, 2, 2, 3, 4, 3, 5, 6 ]
What I have understood from the documentation is $..id is a recursive descent operator that recursively checks for the occurrence of id field in the specified array.
This is where I get the problem. I need an expression that simply checks for id in current object and avoids traversing recursively in images array.
So the expected output is -
[ 1, 2, 3 ]
I tried JSONPath Online Evaluator to verify the results.
Thanks.
According to the documentation of jsonpath they include
.. as Recursive descendant operator;
JSONPath borrows this syntax from E4X
$..* is used when you need to search in All memebers of JSON Structure.
So, if you want restrict the filter at the child you want you need to use $.*.
Here are the syntax
The code you want is:-
const fs = require('fs');
var jp = require('jsonpath');
let rawdata = fs.readFileSync('data.json');
let data = JSON.parse(rawdata);
var id = jp.query(data,'$.*.id');
console.log(id);
Output is
[1,2,3]
You can use the * operator which restricts the filter at the level you need.
To get it to do the thing that you want you would use:
$.*.id
and that will give you the output of:
[
1,
2,
3
]
But let's say you want the image specific id you would use:
$.*.images.*.id
which would give you the output of:
[
1,
2,
3,
4,
5,
6
]

How to filter out the same keys that you $slice in mongodb

I have a collection as this:
[
{_id: "1234", myId: 1, a: [1,2,3], b: [23,3,2], c: [3,234,4], ...},
{_id: "5678", myId: 2, a: [21,32,3], b: [32,123,4], c: [32,32,12], ...},
{_id: "3242", myId: 3, a: [21,23,2], b: [12,2,32], c: [12,213,1], ...}
]
There are many more arrays in each of the document and also the size of each of these arrays is much larger. I want to be able to apply the $slice projection on two of my desired keys (to retrieve 50 latest values) and have only those two keys returned back, using mongo js.
I know how to do the tasks separately, but I'm unable to figure out an intersection of the two .
So using {a: {$slice: -50}, b: {$slice: -50}} as projection in the find() function will return me the last 50 entries, and {a: 1, b: 1} will return me only these two keys from the find() result.
How can I do both simultaneously?
You could try adding a dummy field in your projection:
db.test.find({}, {a: {$slice: -2}, b: {$slice: -2}, dummy: 1, _id: 0})
Returns
/* 0 */
{
"a" : [ 2, 3 ],
"b" : [ 3, 2 ]
}
/* 1 */
{
"a" : [ 32, 3 ],
"b" : [ 123, 4 ]
}
/* 2 */
{
"a" : [ 23, 2 ],
"b" : [ 2, 32 ]
}

MongoDB update query for nest array

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.

Resources