MongoDB - Query on key name of an object - mongodb

I'm new to MongoDB. I'm using laravel-mongodb.
I have a document in tree structure. I need to get documents for a specific year. Here is the image of how my data is:
I need to select all documents that have the period 2017. my problem is that the period is a field of type object. And the value is the index. Does anyone know how I can do this?

You can try below query :
db.collection.aggregate([
/** As we need to filter on key but not on key's value,
* So convert periodos object to an array which makes {k : 2017, v : value of 2017} etc.. */
{
$addFields: {
periodos: {
$objectToArray: "$periodos"
}
}
},
/** Filter docs to check periodos has 2017 */
{
$match: {
"periodos.k": "2017"
}
},
/** convert periodos array back to object to its original type */
{
$addFields: {
periodos: {
$arrayToObject: "$periodos"
}
}
}
])
Test : MongoDB-Playground

You need to use $exists operator
db.collection.find({ "periodos.2017" : { $exists : true } })

Related

Get record having highest date inside nested group in Mongodb

I am having a record set like below :
I need to write a query where foreach datatype of every parent I show the data type with highest date i.e
So far I am able to create two groups one on parent id & other on data type but i am unable to understand how to get record with max date.
Below is my query :
db.getCollection('Maintenance').aggregate( [{ $group :
{ _id :{ parentName: "$ParentID" , maintainancename : "$DataType" }}},
{ $group : {
_id : "$_id.parentName",
maintainancename: {
$push: {
term:"$_id.DataType"
}
}
}
}] )
You don't have to $group twice, try below aggregation query :
db.collection.aggregate([
/** group on two fields `ParentID` & `Datatype`,
* which will leave docs with unique `ParentID + Datatype`
* & use `$max` to get max value on `Date` field in unique set of docs */
{
$group: {
_id: {
parentName: "$ParentID",
maintainancename: "$Datatype"
},
"Date": { $max: "$Date" }
}
}
])
Test : mongoplayground
Note : After group stage you can use $project or $addFieldsstages to transform fields the way you want.

How to rename fields in MongoDB output?

I am new to MongoDB, my native language is Spanish. So I'm not sure how to find what I need.
I will try to make myself understandable.
I do this query and the result is an array.
For example :
Users.find({passport:123, (err, result) => {
//output of result
[
{"nombre":"pedro","apellido":"jose"},
{"nombre":"pablo","apellido":"jacinto"},
{"nombre":"jose","apellido":"berta"},
]
I want to know if there is a more effective way to do something so that using some function from mongodb I can customize the output to avoid doing something like this:
Required Output :
[
{"name":"pedro","lastname":"jose"},
{"name":"pablo","lastname":"jacinto"},
{"name":"jose","lastname":"berta"},
]
is there any way to process the output information directly from mongoDB?
You need to use MongoDB's Aggregation to do that :
db.collection.aggregate([
/** Filter docs based on criteria */
{
$match: {
passport: 123
}
},
/** Transform fields into required form */
{
$project: {
name: "$nombre",
lastname: "$apellido"
}
}
])
Test : MongoDB-Playground
Ref : $match , $project

Query and Update Child Documents without knowing keys

I have a collection with documents having the following format
{
name: "A",
details : {
matchA: {
comment: "Hello",
score: 5
},
matchI: {
score: 10
},
lastMatch:{
score: 5
}
}
},
{
name: "B",
details : {
match2: {
score: 5
},
match7: {
score: 10
},
firstMatch:{
score: 5
}
}
}
I don't immediatly know the name of the keys that are children of details, they don't follow a known format, there can be different amounts etc.
I would like to write a query which will update the children in such a manner that any subdocument with a score less than 5, gets a new field added (say lowScore: true).
I've looked around a bit and I found $ and $elemMatch, but those only work on arrays. Is there an equivalent for subdocuments? Is there some way of doing it using the aggregation pipeline?
I don't think you can do that using a normal update(). There is a way through the aggregation framework which itself, however, cannot alter any persisted data. So you will need to loop through the results and update your documents individually like e.g. here: Aggregation with update in mongoDB
This is the required query to transform your data into what you need for the subsequent update:
collection.aggregate({
$addFields: {
"details": {
$objectToArray: "$details" // transform "details" into uniform array of key-value pairs
}
}
}, {
$unwind: "$details" // flatten the array created above
}, {
$match: {
"details.v.score": {
$lt: 10 // filter out anything that's not relevant to us
// (please note that I used some other filter than the one you wanted "score less than 5" to get some results using your sample data
},
"details.v.lowScore": { // this filter is not really required but it seems to make sense to check for the presence of the field that you want to create in case you run the query repeatedly
$exists: false
}
}
}, {
$project: {
"fieldsToUpdate": "$details.k" // ...by populating the "details" array again
}
})
Running this query returns:
/* 1 */
{
"_id" : ObjectId("59cc0b6afab2f8c9e1404641"),
"fieldsToUpdate" : "matchA"
}
/* 2 */
{
"_id" : ObjectId("59cc0b6afab2f8c9e1404641"),
"fieldsToUpdate" : "lastMatch"
}
/* 3 */
{
"_id" : ObjectId("59cc0b6afab2f8c9e1404643"),
"fieldsToUpdate" : "match2"
}
/* 4 */
{
"_id" : ObjectId("59cc0b6afab2f8c9e1404643"),
"fieldsToUpdate" : "firstMatch"
}
You could then $set your new field "lowScore" using a cursor as described in the linked answer above.

Select data where the range between two different fields contains a given number

I want to make a find query on my database for documents that have an input value between or equal to these 2 fields, LOC_CEP_INI and LOC_CEP_FIM
Example: user input a number to the system with value : 69923994, then I use this input to search my database for all documents that have this value between the range of the fields LOC_CEP_INI and LOC_CEP_FIM.
One of my documents (in this example this document is selected by the query because the input is inside the range):
{
"_id" : ObjectId("570d57de457405a61b183ac6"),
"LOC_CEP_FIM" : 69923999, //this field is number
"LOC_CEP_INI" : 69900001, // this field is number
"LOC_NO" : "RIO BRANCO",
"LOC_NU" : "00000016",
"MUN_NU" : "1200401",
"UFE_SG" : "AC",
"create_date" : ISODate("2016-04-12T20:17:34.397Z"),
"__v" : 0
}
db.collection.find( { field: { $gt: value1, $lt: value2 } } );
https://docs.mongodb.com/v3.2/reference/method/db.collection.find/
refer this mongo provide range facility with $gt and $lt .
You have to invert your field names and query value.
db.zipcodes.find({
LOC_CEP_INI: {$gte: 69923997},
LOC_CEP_FIM: {$lte: 69923997}
});
For your query example to work, you would need your documents to hold an array property, and that each item in this prop hold a 69923997 prop. Mongo would then check that this 69923997 prop has a value that is both between "LOC_CEP_INI" and "LOC_CEP_FIM" for each item in your array prop.
Also I'm not sure whether you want LOC_CEP_INI <= 69923997 <= LOC_CEP_FIM or the contrary, so you might need to switch the $gte and $lte conditions.
db.zipcodes.find( {
"LOC_CEP_INI": { "$lte": 69900002 },
"LOC_CEP_FIM": { "$gte": 69900002 } })
Here is the logic use it as per the need:
Userdb.aggregate([
{ "$match": { _id: ObjectId(session._id)}},
{ $project: {
checkout_list: {
$filter: {
input: "$checkout_list",
as: "checkout_list",
cond: {
$and: [
{ $gte: [ "$$checkout_list.createdAt", new Date(date1) ] },
{ $lt: [ "$$checkout_list.createdAt", new Date(date2) ] }
]
}
}
}
}
}
Here i use filter, because of some reason data query on nested data is not gets succeed in mongodb

Cant group with aggregate in mongodb

I try to aggregate and group objects in mongodb by month. I basically copy query from mongo docs.
db.users.aggregate(
{
$group : {
_id: {
month : { $month : "$registrationDate" }
},
count: { $sum: 1 }
}
}
);
Type of registrationDate is date.
Short version of object in users collection.
{
"_id" : ObjectId("50ab08399b57f2be03000000"),
...
"registrationDate" : ISODate("2012-11-20T05:34:01.000Z"),
...
}
Then I get an exception
exception: can't convert from BSON type NumberDouble to Date
The problem is that you have some documents in your collection where the type of registrationDate is not a date but a double-precision floating point number. You can find these documents with db.users.find( { registrationDate: { $type:1 } } ). Fix these documents and it should work. Alternatively you can add the following step to the front of your aggregation to exclude those documents where the registrationDate is not a Date: {$match: { registrationDate: { $type:9 } } }