MongoDB: top-level array conditions - arrays

I have an array of arrays in MongoDB 4.2.2:
db.foo.insertOne({'foo': [[1, 3], [2], [3]]})
I'd like to remove elements of foo, first elements of which are greater than 1. But I cannot figure out how.
I tried something like this (and many more) but it does not pull anything:
db.foo.update({}, {$pull: {foo: {'0': {$gt: 1}}}})
Is it possible?
EDIT: Expected result:
db.foo.find({})
{ "_id": ObjectId("..."), "foo": [ [1, 3] ] }

If you are using MongoDB 4.2, you can make use of a pipeline in the new update. This allows you to pass an aggregation pipeline as the update argument:
db.foo.update({},[
{$addFields:{
foo:{
$filter:{
input:"$foo",
cond:{
$lte: [{$arrayElemAt: ["$$this", 0]}, 1]
}
}
}
}}
])

Related

MongoDB query for nested array of specific object

I am a new mongodb user, this why I am asking this question. I have a document, in this document I have 3 objects under one _id.
When I am filtering { "people.age": { $in: [24] } } I am getting full this document. But I want to see only the matching object. Like for age 24, I just want to see object 2, not object 0 and 1.
Is it possible to show only the matching object? If you kindly explain me it will be helpful for me.
Use $ for projection.
Query 1
db.collection.find({
"people.age": {
$in: [
24
]
}
},
{
"people.$": 1
})
Sample Mongo Playground (Query 1)
If you just to search people by certain age, you may use the below query as well:
Query 2
db.collection.find({
"people.age": 24
},
{
"people.$": 1
})
Sample Mongo Playground (Query 2)
Note: $ will returns only the first element of the array.
You may look for aggregation query as:
$match - Filter the document by age.
$project - Decorate output documents. With $filter operator to filter the document in people array.
db.collection.aggregate([
{
$match: {
"people.age": 24
}
},
{
$project: {
"people": {
$filter: {
input: "$people",
cond: {
$eq: [
"$$this.age",
24
]
}
}
}
}
}
])
Sample Mongo Playground (Aggregation pipeline)
Reference
Project Specific Array Elements in the Returned Array

How to access nested data in Arrays and Objects with Javascript

I am tring to use react-charts and the object they give as example data looks like this.
chartData: [
{
label: 'Series 1',
data: [
[0, 1],
[1, 2],
[2, 4],
[3, 2],
[4, 7],
],
},
],
I want to build my own "data" and replace this temporary data but the whole object of arrays in objects in arrays (Or whatever it is confuses me.
Can someone explain the nesting here.
Is it an array of 2 objects label and data and data` is an array of key value pairs. Is that accurate?
I'm kind of trying something like this...
let myData = []
res.data.payload.forEach(function (item, index) {
console.log(item, index)
myData[(index, item.odds)]
})
this.setState({ chartData[data]: myData })
Am I even close?
Thanks
You can do like this
let myData = []
res.data.payload.forEach(function (item, index) {
console.log(item, index)
myData.push([index, item.odds])
})
this.setState({ chartData: [{...this.state.chartData[0], data: myData}] })

Converting array to json in Ruby (Puppet, facter)

I'm writing a fact for Puppet in Ruby. I have an array
array = [[["User", "Username"], ["Date", "16.12.2014"]], [["User1", "Username1"], ["Date1", "17.12.2014"]]]
I want to convert it to json. I tried to convert it to hash first, but doing like this in Linux
array.each do |userarr|
winusers = Hash[userarr.map! { |pair| [pair[0], pair[1]] } ]
end
I get only the this one [["User1", "Username1"], ["Date1", "17.12.2014"]] pair converted. Doing like this:
array.each do |userarr|
winusers = Hash[userarr.map! { |pair| [pair[0], pair[1]] } ]
winusersa << winusers
end
I get an array of hashes. Coverting it to json winusersa.to_json on Linux I get an array of json format text, on Puppet (facter in fact) I get only the first pair converted. Why in Puppet fact it doesn't work? How to convert that array to get all pairs well formated?
Try this one
array.flatten(1).each_slice(2).map(&:to_h)
=> [{"User"=>"Username", "Date"=>"16.12.2014"}, {"User1"=>"Username1", "Date1"=>"17.12.2014"}]
And then, as an hash, you can easily call to_json
You've already got your Array in the form that the ruby Hash#[] method can consume. I think all you need is this:
% pry
[1] pry(main)> require 'json'
[2] pry(main)> a = [[["User", "Username"], ["Date", "16.12.2014"]], [["User1", "Username1"], ["Date1", "17.12.2014"]]]
[3] pry(main)> puts JSON.pretty_generate(a.map { |e| Hash[e] })
[
{
"User": "Username",
"Date": "16.12.2014"
},
{
"User1": "Username1",
"Date1": "17.12.2014"
}
]
Require 'facter' #if you have facter as gem to test locally require 'json'
array = [
[
["User", "Username"],
["Date", "16.12.2014"]
],
[
["User1", "Username1"],
["Date1", "17.12.2014"]
]
]
put JSON.pretty_generate(JSON.parse(array.to_json))

Convert Array of Arrays into JSON

I have an array of arrays that I'd like to convert into json and output within another array. I have the following array:
weekdays = [["Monday",2],["Tuesday",4],["Thursday",5]]
I would like to include this array within a JSON output like so:
json_output = { :results => weekdays.count, :data => weekdays }
Right now I get this, which just doesn't look right as there are not curly brackets around the "data" field...
{
"results": 2,
"data": [
["Monday", 2],
["Tuesday", 4],
["Thursday", 5]
]
}
Any help would be great!
The output is correct. Curly brackets are around hashes, but your data attribute is a nested array.
If you want to convert a nested array into a hash, just call to_h on it:
{ :results => weekdays.count, :data => weekdays.to_h }
Better to convert it to hash manually.
weekdays = [["Monday",2],["Tuesday",4],["Thursday",5]]
hash_weekdays = Hash.new
weekdays.each do |item|
hash_weekdays[item[0]] = item[1]
end
hash_weekdays #=> {"Monday"=>2, "Tuesday"=>4, "Thursday"=>5}

MongoDB - Query on the last element of an array?

I know that MongoDB supports the syntax find{array.0.field:"value"}, but I specifically want to do this for the last element in the array, which means I don't know the index. Is there some kind of operator for this, or am I out of luck?
EDIT: To clarify, I want find() to only return documents where a field in the last element of an array matches a specific value.
In 3.2 this is possible. First project so that myField contains only the last element, and then match on myField.
db.collection.aggregate([
{ $project: { id: 1, myField: { $slice: [ "$myField", -1 ] } } },
{ $match: { myField: "myValue" } }
]);
You can use $expr ( 3.6 mongo version operator ) to use aggregation functions in regular query.
Compare query operators vs aggregation comparison operators.
For scalar arrays
db.col.find({$expr: {$gt: [{$arrayElemAt: ["$array", -1]}, value]}})
For embedded arrays - Use $arrayElemAt expression with dot notation to project last element.
db.col.find({$expr: {$gt: [{"$arrayElemAt": ["$array.field", -1]}, value]}})
Spring #Query code
#Query("{$expr:{$gt:[{$arrayElemAt:[\"$array\", -1]}, ?0]}}")
ReturnType MethodName(ArgType arg);
Starting Mongo 4.4, the aggregation operator $last can be used to access the last element of an array:
For instance, within a find query:
// { "myArray": ["A", "B", "C"] }
// { "myArray": ["D"] }
db.collection.find({ $expr: { $eq: [{ $last: "$myArray" }, "C"] } })
// { "myArray": ["A", "B", "C"] }
Or within an aggregation query:
db.collection.aggregate([
{ $addFields: { last: { $last: "$myArray" } } },
{ $match: { last: "C" } }
])
use $slice.
db.collection.find( {}, { array_field: { $slice: -1 } } )
Editing:
You can make use of
{ <field>: { $elemMatch: { <query1>, <query2>, ... } } } to find a match.
But it won't give exactly what you are looking for. I don't think that is possible in mongoDB yet.
I posted on the official Mongo Google group here, and got an answer from their staff. It appears that what I'm looking for isn't possible. I'm going to just use a different schema approach.
Version 3.6 use aggregation to achieve the same.
db.getCollection('deviceTrackerHistory').aggregate([
{
$match:{clientId:"12"}
},
{
$project:
{
deviceId:1,
recent: { $arrayElemAt: [ "$history", -1 ] }
}
}
])
You could use $position: 0 whenever you $push, and then always query array.0 to get the most recently added element. Of course then, you wont be able to get the new "last" element.
Not sure about performance, but this works well for me:
db.getCollection('test').find(
{
$where: "this.someArray[this.someArray.length - 1] === 'pattern'"
}
)
You can solve this using aggregation.
model.aggregate([
{
$addFields: {
lastArrayElement: {
$slice: ["$array", -1],
},
},
},
{
$match: {
"lastArrayElement.field": value,
},
},
]);
Quick explanations. aggregate creates a pipeline of actions, executed sequentially, which is why it takes an array as parameter. First we use the $addFields pipeline stage. This is new in version 3.4, and basically means: Keep all the existing fields of the document, but also add the following. In our case we're adding lastArrayElement and defining it as the last element in the array called array. Next we perform a $match pipeline stage. The input to this is the output from the previous stage, which includes our new lastArrayElement field. Here we're saying that we only include documents where its field field has the value value.
Note that the resulting matching documents will include lastArrayElement. If for some reason you really don't want this, you could add a $project pipeline stage after $match to remove it.
For the answer use $arrayElemAt,if i want orderNumber:"12345" and the last element's value $gt than "value"? how to make the $expr? thanks!
For embedded arrays - Use $arrayElemAt expression with dot notation to project last element.
db.col.find({$expr: {$gt: [{"$arrayElemAt": ["$array.field", -1]}, value]}})
db.collection.aggregate([
{
$match: {
$and: [
{ $expr: { $eq: [{ "$arrayElemAt": ["$fieldArray.name", -1] }, "value"] } },
{ $or: [] }
]
}
}
]);

Resources