Matching in MongoDB returns unwanted results - mongodb

I have a collection named flights. In that collection I have two fields: origin_country and the dest_country.
These fields just keep record of the origin country and the destination country of a particular flight.
I'm trying to write a query which will return:
All theinternational flights only (i.e - where the origin and destination countries are different).
The sum of each (i.e - # of occurences of that flight in the collection).
The problem with the query which I have already is that it's also returning the domestic flights when ran:
QUERY:
db.flights.aggregate([
{$match: {
checking_countries_dont_match: { $ne: ["origin_country", "dest_country"] }
} },
{$group: {
_id: {
origin_country: "$origin_country",
dest_country: "$dest_country"
},
"count": {$sum:1}
} },
{$sort: {"count":-1} }
])
DOCUMENT SAMPLES:
{
"_id" : "2675594611",
"origin_country" : "Germany",
"dest_country" : "United Arab Emirates",
"airline_name" : "etihad-airways-etd"
}
{
"_id" : "2661517182",
"origin_country" : "Thailand",
"dest_country" : "Thailand",
"airline_name" : "nok-air-nok",
}
UPDATE
I changed the query to the following, but I still get results where the origin and destination are the same:
db.flights.aggregate([
{ $project: {
dont_match: { $ne: ["origin_country", "dest_country"] },
origin_country: "$origin_country",
dest_country: "$dest_country",
airline_name: "$airline_name"
} },
{ $match: {
airline_name: "etihad-airways-etd",
dont_match: true
} },
{ $group: {
id: {
origin_country: "$origin_country",
dest_country: "$dest_country"
}
} }
]);
UPDATE 2
Wokring Query:
db.flights.aggregate([
{ $project: {
dont_match: { $ne: ["$origin_country", "$dest_country"] },
origin_country: "$origin_country",
dest_country: "$dest_country",
airline_name: "$airline_name"
} },
{ $match: {
airline_name: "etihad-airways-etd",
dont_match: true
} },
{ $group: {
_id: {
origin_country: "$origin_country",
dest_country: "$dest_country"
},
count: {$sum:1}
} },
{ $sort: {count:-1}}
]);
Thanks for the help everyone :)

All you need to do is to modify your query a little bit. Look at this example, should give you an idea:
db.flights.aggregate([
{
$project: {
dont_match: { $ne: ["$origin_country", "$dest_country"] },
...
}
},
{
$match: {
dont_match: true
}
},
...
]);

Can you please try below query whether it match your expectation or not:
db.flights.aggregate([
{ $project: {
dont_match: {$cmp: ['$origin_country', '$dest_country']},
origin_country: "$origin_country",
dest_country: "$dest_country"
} },
{ $match: { dont_match: {$ne: 0} } }
]);

Related

Mongoldb aggregation average of a number in an array?

I have a collection of documents that look like this
{
_id : 21353456,
product : "xy",
text : "asdf",
reviews : [
{
username : "User1",
userID: 12
text : "hi"
rate: 4,
},
{
username : "User2",
userID: 123
text : "hi1"
rate:2,
}
]
}
I want to retrieve the average rating for user1 on all the product they have rated.
db.collection.aggregate([{$unwind: "$reviews"},{$match: {"$review.userID": "12"}},{$group: { _id: "$reviews.userName",
{avgRate: {$avg: "$reviews.rate"}}})]
)
I tried this but I keep getting unexpected token errors for "," where the last ")" is.
You did this:
db.collection.aggregate([
{ $unwind: "$reviews" },
{ $match: { "$review.userID": "12" } },
{ $group: { _id: "$reviews.userName",
{ avgRate: { $avg: "$reviews.rate" } }})]
)
Must be this:
db.collection.aggregate([
{ $unwind: "$reviews" },
{ $match: { "$review.userID": "12" } },
{
$group: {
_id: "$reviews.userName",
avgRate: { $avg: "$reviews.rate" }
}
}
])

MongoDB Aggregation to get count and Y sample entries

MongoDB version:4.2.17.
Trying out aggregation on data in a collection.
Example data:
{
"_id" : "244",
"pubName" : "p1",
"serviceIdRef" : "36e9c779-7865-4b74-a30b-e4d6a0cc5295",
"serviceName" : "my-service",
"subName" : "c1",
"pubState" : "INVITED"
}
I would like to:
Do a match by something (let’s say subName) and group by serviceIdRef and then limit to return X entries
Also return for each of the serviceIdRefs, the count of the documents in each of ACTIVE or INVITED states. And Y (for this example, say Y=3) documents that are in this state.
For example, the output would appear as (in brief):
[
{
serviceIdRef: "36e9c779-7865-4b74-a30b-e4d6a0cc5295",
serviceName:
state:[
{
pubState: "INVITED"
count: 200
sample: [ // Get those Y entries (here Y=3)
{
// sample1 like:
"_id" : "244",
"pubName" : "p1",
"serviceIdRef" : "36e9c779-7865-4b74-a30b-e4d6a0cc5295",
"serviceName" : "my-service",
"subName" : "c1",
"pubState" : "INVITED"
},
{
sample2
},
{
sample3
}
]
},
{
pubState: "ACTIVE", // For this state, repeat as we did for "INVITED" state above.
......
}
]
}
{
repeat for another service
}
]
So far I have written this but am not able to get those Y entries. Is there a (better) way?
This is what I have so far (not complete and not exactly outputs in the format above):
db.sub.aggregate(
[{
$match:
{
"subName": {
$in: ["c1", "c2"]
},
"$or": [
{
"pubState": "INVITED",
},
{
"pubState": "ACTIVE",
}
]
}
},
{
$group: {
_id: "$serviceIdRef",
subs: {
$push: "$$ROOT",
}
}
},
{
$sort: {
_id: -1,
}
},
{
$limit: 22
},
{
$facet:
{
facet1: [
{
$unwind: "$subs",
},
{
$group:
{
_id: {
"serviceName" : "$_id",
"pubState": "$subs.pubState",
"subState": "$subs.subsState"
},
count: {
$sum: 1
}
}
}
]
}
}
])
You have to do the second $group stage to manage nested structure,
$match your conditions
$sort by _id in descending order
$group by serviceIdRef and pubState, get first required fields and prepare the array for sample, and get count of documents
$group by only serviceIdRef and construct the state array
$slice for limit the document in sample
db.collection.aggregate([
{
$match: {
subName: { $in: ["c1", "c2"] },
pubState: { $in: ["INVITED", "ACTIVE"] }
}
},
{ $sort: { _id: -1 } },
{
$group: {
_id: {
serviceIdRef: "$serviceIdRef",
pubState: "$pubState"
},
serviceName: { $first: "$serviceName" },
sample: { $push: "$$ROOT" },
count: { $sum: 1 }
}
},
{
$group: {
_id: "$_id.serviceIdRef",
serviceName: { $first: "$serviceName" },
state: {
$push: {
pubState: "$_id.pubState",
count: "$count",
sample: { $slice: ["$sample", 22] }
}
}
}
}
])
Playground

About Mongo Group by where arr.length>0

Just assume following data:
{_id:1,hotelcode:a,availdates:["2020-01-02","2020-02-03"]}
{_id:2,hotelcode:a,availdates:["2020-02-03"]}
{_id:3,hotelcode:b,availdates:[]}
{_id:4,hotelcode:b,availdates:["2020-01-02"]}
{_id:5,hotelcode:c,availdates:["2020-01-02","2020-02-03"]}
I wanna achieve:
select hotelcode,count(hotelcode) from table group by hotelcode where availdates.length>0
What should I do?
I tried:
db.getCollection('spl_rate_27').aggregate([
{$project:{
adlength:{$size:"$avail_dates"}}
},
{$match:{adlength:{$gt:1}}},
{$group:{_id:{hotelcode:"$hotel_code"},total:{$sum:1}}}
])
But I got :
{
"_id" : {
"hotelcode" : null
},
"total" : 99999,0
}
It seems something was wrong...But I can't find it out....
You can do something like following, first get the objects whose availdates is greater than 0
[
{
$match: {
$expr: {
$gt: [
{
$size: "$availdates"
},
0
]
}
}
},
{
$group: {
_id: "$hotelcode",
total: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
hotelcode: "$_id",
total: 1
}
}
]
Working Mongo playground
There are two things you can change.
Instead of $project use $addFields - project restricts fields, addFields adds field to the document
Then use $gte in the query as you need >0.
play
db.collection.aggregate([
{
$addFields: {
adlength: {
$size: "$availdates" //misspelled
}
}
},
{
$match: {
adlength: {
$gte: 1
}
}
},
{
$group: {
_id: {
hotelcode: "$hotelcode" //misspelled
},
total: {
$sum: 1
}
}
}
])
Well , I got inspiration from #Gibbs' answer. And I changed a bit my script:
db.getCollection('table').aggregate([
{$project:{
hotelcode:1, ##I omit this!!!
adlength:{$size:"$availdates"}}
},
{$match:{"adlength":{$gt:0}}},
{$group:{_id:{hotelcode:"$hotelcode"},total:{$sum:1}}}
])
And it works perfectly!
I hope this is what you are expecting.
db.collection.aggregate({
$match: {
"availdates": {
"$gt": "1"
}
}
},
{
$group: {
_id: "$hotelcode",
"records": {
$push: "$$ROOT"
},
"dataCount": {
$sum: 1
}
}
})
Working demo url : Mongo Playground URL

MongoDB: elemMatch match the last element in an array

I have the data like below:
{
"order_id" : 1234567,
"order_pay_time" : 1437373297,
"pay_info" : [
{
"pay_type" : 0,
"pay_time" : 1437369046
},
{
"pay_type" : 0,
"pay_time" : 1437369123
},
{
"pay_type" : 0,
"pay_time" : 1437369348
}
]}
what I want to get is the last payment is of type 1, but $elemMatch just match the list where pay_type:1 exists, how can I match the orders which last payment is of "pay_type" : 1
You can use aggregation to get expected output. The query will be like following:
db.collection.aggregate({
$unwind: "$pay_info"
}, {
$match: {
"pay_info.pay_type": 1
}
}, {
$group: {
_id: "$_id",
"pay_info": {
$push: "$pay_info"
},
"order_id": {
$first: "$order_id"
},
"order_pay_time": {
$first: "$order_pay_time"
}
}
})
Moreover if you want latest pay_info.pay_time then you can sort it by descending order with limit 1, some what like following:
db.collection.aggregate({
$unwind: "$pay_info"
}, {
$match: {
"pay_info.pay_type": 1
}
}, {
$sort: {
"pay_info.pay_time": -1
}
}, {
$limit: 1
}, {
$group: {
_id: "$_id",
"pay_info": {
$push: "$pay_info"
},
"order_id": {
$first: "$order_id"
},
"order_pay_time": {
$first: "$order_pay_time"
}
}
})
Edit
Also you can use $redact to avoid $unwind like following:
db.collection.aggregate({
$match: {
"pay_info": {
$elemMatch: {
"pay_type": 1
}
}
}
}, {
$sort: {
"pay_info.pay_time": -1
}
}, {
$limit: 1
}, {
$redact: {
$cond: {
if: {
$eq: [{
"$ifNull": ["$pay_type", 1]
}, 1]
},
then: "$$DESCEND",
else: "$$PRUNE"
}
}
}).pretty()
Just found this thread for a similar problem I've had.
I ended up doing this, maybe that will be of interest to someone:
db.collection.find({
$where: function(){
return this.pay_info[this.pay_info.length-1].pay_type === 1
}
})

Mongodb aggregate query issue

I have the following query which is supposed to be getting, for each state, the amount of flights that fly within the state. Basically, the origin state and the destination state are the same. Pretty straightforward.
function(){
return db.flight_info.aggregate([
{ $project: { matches: { $eq:[ '$ORIGIN_STATE_ABR', '$DEST_STATE_ABR' ] }} },
{ $match: { matches:true } },
{ $group: { _id: "$ORIGIN_STATE_ABR", total: {$sum: 1} } }
]);
}
The issue is that this is not actually grouping my data for some reason. The output simply looks like this:
{
"_id": null,
"total": 61402
}
while it should be something along the lines of:
{
{"_id": "FL",
"total": 620 },
{"_id": "GA",
"total": 896},
...
}
You're missing the ORIGIN_STATE_ABR in your $project and that's causing an issue in your grouping.
db.flight_info.aggregate([
{ $project: { ORIGIN_STATE_ABR: 1,
matches: { $eq:[ '$ORIGIN_STATE_ABR', '$DEST_STATE_ABR' ] } } },
{ $match: { matches:true } },
{ $group: { _id: "$ORIGIN_STATE_ABR", total: { $sum: 1 } } }
]);