MongoDB Retrieve single value from array - arrays

Assuming that if i have such document
{ "id":"1", "references":["AD1","AD2","AD3"] }
I would like to retrieve single value ("AD1") within the array. is there anyway that i can do that with mongodb query? i have use various way but that it would instead return me the whole array instead of individual single value.

You can do this via $project and $filter:
db.collection.aggregate([
{
$project: {
references: {
$filter: {
input: "$references",
as: "ref",
cond: { $eq: [ "AD1", "$$ref" ] }
}
}
}
}
])
You can see it working here
Note however that $filter is available in mongoDB 3.2 version and higher

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 push all set of record into one array using MongoDB query

I need to push all records into one array while executing the mongoDB query. I am explaining my query below.
db.getCollection('orders').aggregate([
{
$match: {"Customer.StoreCode":"CMHB"}
},
{
$group: {_id : "$Customer.CustomerMobile", "data" : {"$first" : "$$ROOT"}}
},
{
$project: {
OrderNumber:"$data.OrderNumber",
OrderStatus:"$data.OrderStatus",
OrderType:"$data.OrderType",
CreatedAt:"$data.CreatedAt",
CustomerMobile: "$data.Customer.CustomerMobile",
CustomerLastName:"$data.Customer.CustomerLastName",
CustomerFirstName:"$data.Customer.CustomerFirstName",
StoreCode:"$data.Customer.StoreCode",
TransactionId:"$data.PaymentDetails.TransactionId",
PaymentStatus:"$data.PaymentDetails.PaymentStatus",
PaymentAmount:"$data.PaymentDetails.PaymentAmount",
ItemNos: { $cond: { if: { $isArray: "$data.Products" }, then: { $size: "$data.Products" }, else: "NA"} }
}
}
])
Here I am getting Some records as per store code and mobile no. Here I need to push all records into one array using that same query.
MongoDB has the function toArray() you can do this
db.getCollection().toArray();
https://docs.mongodb.com/manual/reference/method/cursor.toArray/

How to access the elements in a mongodb array

I have a mongodb Document like the one mentioned below
{
"_id" : ObjectId("213122423423423"),
"eventDateTimes" :
[
ISODate("2015-05-26T12:18:15.000Z"),
ISODate("2015-05-26T12:18:14.000Z"),
ISODate("2015-05-26T12:00:37.000Z"),
ISODate("2015-05-26T12:00:36.000Z")
],
"parseFlags" :
[
false,
"True",
false,
false
],
"eventMessages" : [
"Erro1 ",
"Error2",
"Error3",
"Error4"
]
}
}
I have to fetch the eventMessages based on the parseFlags array.
I have to get the index of elements in the parseFlags array where the value is "false" and then get the event messages mapping to those indexes.
The result should be the document with following parameters where parseFlag is false:
{
id,
EventDateTimes:[date1,date3,date4],
ParseFlags :[false,false,false]
eventMessages :[Error1,Error3,Error4]
}
Can you please let me know how to get this output? I am using mongodb 3.2.
Mongodb 3.4 has new array operator zip that groups the array elements based on the index
db.collectionName.aggregate({
$project: {
transposed: {
$zip: {inputs: ["$eventDateTimes", "$parseFlags", "$eventMessages"]
}
}
},
{$unwind: '$transposed'},
{$project: {eventDateTime: {$arrayElemAt: ['$tranposed',0]}, parseFlag:
{$arrayElemAt: ['$transposed', 1]}}}
{$match: {parseFlag: false}}
)
For mongo 3.2 there isn't any direct way to handle the expected query. You can instead use mapReduce with custom code
db.collection.mapReduce(function(){
var transposed = [];
for(var i=0;i<this.eventDateTimes.length;i++){
if(this.parseFlags[i]===false){
transposed.push({eventDateTime: this.eventDateTimes[i], parseFlag: this.parseFlags[i]});
}
}
emit(this._id, transposed);
}, function(key, values){
return [].concat.apply([], values);
}, {out: {inline:1}})
PS: I haven't actually executed the query, but above one should give you an idea how it needs to be done.

MongoDB find all not in this array

I'm trying to find all users except for a few, like this:
// get special user IDs
var special = db.special.find({}, { _id: 1 }).toArray();
// get all users except for the special ones
var users = db.users.find({_id: {$nin: special}});
This doesn't work because the array that I'm passing to $nin is not and array of ObjectId but an array of { _id: ObjectId() }
Variable special looks like this after the first query:
[ { _id: ObjectId(###) }, { _id: ObjectId(###) } ]
But $nin in the second query needs this:
[ ObjectId(###), ObjectId(###) ]
How can I get just the ObjectId() in an array from the first query so that I can use them in the second query?
Or, is there a better way of achieving what I'm trying to do?
Use the cursor.map() method returned by the find() function to transform the list of { _id: ObjectId(###) } documents to an array of ObjectId's as in the following
var special = db.special.find({}, { _id: 1 }).map(function(doc){
return doc._id;
});
Another approach you can consider is using the $lookup operator in the aggregation framework to do a "left outer join" on the special collection and filtering the documents on the new "joined" array field. The filter should match on documents whose array field is empty.
The following example demonstrates this:
db.users.aggregate([
{
"$lookup": {
"from": "special",
"localField": "_id",
"foreignField": "_id",
"as": "specialUsers" // <-- this will produce an arry of "joined" docs
}
},
{ "$match": { "specialUsers.0": { "$exists": false } } } // <-- match on empty array
])

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