Mongodb aggregate - using $and and $eq inside a $match operator - mongodb

I wanted to match and get the documents I wanted based on 2 conditions
data
But it seems like the $eq inside $match isn't doing the right job, I checked the docs many times but can't find what exactly is the problem.
[
{
"episode_title":"Episode Title 1",
"episode_number":"01",
"unique_id":"5D66EEDA9BF055BE80CB997EFD571636C02ED050",
"series_id":1,
"comments_hash":"CBBEA21FE1E4",
"quality":1080,
"thumbnail_url":null,
"thumbnail_version":null,
"original_thumbnail":null,
"watched_by":[
1820242622
]
},
{
"episode_title":"Episode Title 2",
"episode_number":"02",
"unique_id":"12DC0F2B6D1223D8FF6C10189B6CF6DEB4BBA60B",
"series_id":1,
"comments_hash":"EEDA9BF055BE80C",
"quality":720,
"thumbnail_url":null,
"thumbnail_version":null,
"original_thumbnail":null,
"watched_by":[
1820242622
]
},
{
"episode_title":"Episode Title 1",
"episode_number":"01",
"unique_id":"635F12999D0641C95CBBEA21FE1E46BF26BDCCCA",
"series_id":2,
"comments_hash":"7EFD571636",
"quality":1080,
"thumbnail_url":null,
"thumbnail_version":null,
"original_thumbnail":null,
"watched_by":[
1820242622
]
}
]
The data in USERS collection looks something like this:
[
{
"user_id":1820242622,
"full_name":"user 1",
"start_date":"2021-11-18T19:36:27.695953",
"is_banned":false,
"preferred_quality":1080
},
{
"user_id":1820242624,
"full_name":"user 2",
"start_date":"2021-11-18T19:36:27.695953",
"is_banned":false,
"preferred_quality":0
}
]
and I used this code (pymongo):
user_id = 1820242622
series_id = 1
db.data_col.aggregate([
{
"$lookup":{
"from":"USERS",
"pipeline":[
{
"$match":{
"user_id":"user_id"
}
},
{
"$project":{
"preferred_quality":1
}
}
],
"as":"preferred_quality"
}
},
{
"$match":{
"$and":[
{
"$eq":[
"$series_id",
"series_id"
]
},
{
"$in":[
"$quality",
[
0,
"$preferred_quality"
]
]
}
]
}
},
{
"$set":{
"preferred_quality":"$preferred_quality"
}
},
{
"$set":{
"user_watched":{
"$in":[
"user_id",
"$watched_by"
]
}
}
},
{
"$set":{
"watched_by":"$$REMOVE"
}
}
])
but I'm getting:
raise OperationFailure(errmsg, code, response, max_wire_version) pymongo.errors.OperationFailure: unknown top level operator: $eq. If you have a field name that starts with a '$' symbol, consider using $getField or $setField., full error: {'ok': 0.0, 'errmsg': "unknown top level operator: $eq. If you have a field name that starts with a '$' symbol, consider using $getField or $setField.", 'code': 2, 'codeName': 'BadValue', '$clusterTime': {'clusterTime': Timestamp(1648755167, 3), 'signature': {'hash': b"\x81\xd59\xae\x04'\x15\xac0\x08X6\x86\xc8\xa4\xf22\x07\x87\xd2", 'keyId': 7037880200322875408}}, 'operationTime': Timestamp(1648755167, 3)}
I was expecting something like this:
{
"episode_title":"Episode Title 1",
"episode_number":"01",
"unique_id":"635F12999D0641C95CBBEA21FE1E46BF26BDCCCA",
"series_id":1,
"comments_hash":"7EFD571636",
"quality":1080,
"thumbnail_url":null,
"thumbnail_version":null,
"original_thumbnail":null,
"preferred_quality":1080,
"user_watched":false
}

db.data_col.aggregate([
{
$match: {
"unique_id": "635F12999D0641C95CBBEA21FE1E46BF26BDCCCA"
}
},
{
$lookup: {
from: "USERS",
localField: "watched_by",
foreignField: "user_id",
as: "preferred_quality"
}
},
{
$set: {
preferred_quality: {
$first: "$preferred_quality.preferred_quality"
}
}
}
])
mongoplayground

Related

mongodb aggregation framework $match - $lookup - $project (failing to use $project for structuring the result)

Sample Data:
users collection:
[
{
"_id":ObjectId("614804bb4236d9b0896f4e7c"),
"firstName":"Fabian",
"lastName":"Barlow"
},
{
"_id":ObjectId("614804bb4236d9b0896f4e7d"),
"firstName":"Chloe",
"lastName":"Mendez"
},
{
"_id":ObjectId("614804bb4236d9b0896f4e7e"),
"firstName":"Valentine",
"lastName":"Sharp"
}
]
bookings collection:
[
{
"_id":ObjectId("614805534236d9b0896f4e86"),
"resourceName":"res-1",
"duration":"10 days",
"status":"active",
"bookingCreatorId":ObjectId("614804bb4236d9b0896f4e7c")
},
{
"_id":ObjectId("614805534236d9b0896f4e87"),
"resourceName":"res-2",
"duration":"15 days",
"status":"active",
"bookingCreatorId":ObjectId("614804bb4236d9b0896f4e7d")
},
{
"_id":ObjectId("614805534236d9b0896f4e88"),
"resourceName":"res-3",
"duration":"15 days",
"status":"completed",
"bookingCreatorId":ObjectId("614804bb4236d9b0896f4e7d")
}
]
I am trying to perform an aggregation query on the above collection "bookings", to get the result in a particular structure,
While using project i am trying to add a new field "bookingCreatorDetails" which needs to hold data from "users" collections, but instead of getting the correct data for users collections, i am getting "Array" type for the fields inside "bookingCreatorDetails"
Attempt on creating the query ("bookings"):
[
{
'$match': {
'status': 'active'
}
}, {
'$lookup': {
'from': 'users',
'localField': 'bookingCreatorId',
'foreignField': '_id',
'as': 'bookingCreator'
}
}, {
'$project': {
'resourceName': 1,
'duration': 1,
'bookingCreatorId': 1,
'bookingCreatorDetails.firstName': '$bookingCreator.firstName',
'bookingCreatorDetails.lastName': '$bookingCreator.lastName'
}
}
]
Current incorrect result:
[
{
"_id":ObjectId("614805534236d9b0896f4e86"),
"resourceName":"res-1",
"duration":"10 days",
"status":"active",
"bookingCreatorId":ObjectId("614804bb4236d9b0896f4e7c"),
"bookingCreatorDetails": {
"firstName":Array,
"lastName":Array
}
},
{
"_id":ObjectId("614805534236d9b0896f4e87"),
"resourceName":"res-2",
"duration":"15 days",
"status":"active",
"bookingCreatorId":ObjectId("614804bb4236d9b0896f4e7d"),
"bookingCreatorDetails": {
"firstName":Array,
"lastName":Array
}
}
]
Expected Result:
[
{
"_id":ObjectId("614805534236d9b0896f4e86"),
"resourceName":"res-1",
"duration":"10 days",
"status":"active",
"bookingCreatorId":ObjectId("614804bb4236d9b0896f4e7c"),
"bookingCreatorDetails": {
"firstName":"Fabian",
"lastName":"Barlow"
}
},
{
"_id":ObjectId("614805534236d9b0896f4e87"),
"resourceName":"res-2",
"duration":"15 days",
"status":"active",
"bookingCreatorId":ObjectId("614804bb4236d9b0896f4e7d"),
"bookingCreatorDetails": {
"firstName":"Chloe",
"lastName":"Mendez"
}
}
]
The $lookup will always return in array of objects format, You can use $arrayElemAt operator to select first element from the array or $first operator from 4.4,
using $arrayElemAt operator
{
"$project": {
"resourceName": 1,
"duration": 1,
"bookingCreatorId": 1,
"bookingCreatorDetails": {
$arrayElemAt: ["$bookingCreator", 0]
}
}
}
using $first operator
{
"$project": {
"resourceName": 1,
"duration": 1,
"bookingCreatorId": 1,
"bookingCreatorDetails": {
$first: "$bookingCreator"
}
}
}
Out of the question if you want to remove _id from lookup result you can use $unset stage after your $project stage,
{ $unset: "bookingCreatorDetails._id" }

Mongodb Joining multi document with $lookup aggregation operator and search in the joined document using $or at once

Let's say I have a Purchase order document and this purchase order document has Ids of the supplier and outlet which both of them are separated document (*not embedded).
What I want when user query like this from client-side "give me the purchase order documents which (purchase-order reference-number or supplier name or outlet name) is like "Otilia"
The aggregation query I made so far is this:
[
{
"$lookup":{
"from":"outlets",
"localField":"outlet",
"foreignField":"_id",
"as":"outlets"
}
},
{
"$lookup":{
"from":"suppliers",
"localField":"supplier",
"foreignField":"_id",
"as":"suppliers"
}
},
{
"$match":{
"$and":[
{
"$or":[
{
"outlets.name":{
"$regex":".*Otilia.*",
"$options":"i"
},
"referenceNumber":{
"$regex":".*Otilia.*",
"$options":"i"
},
"suppliers.name":{
"$regex":".*Otilia.*",
"$options":"i"
}
}
]
}
]
}
},
{
"$sort":{
"outlets.name":1,
"_id":1
}
}
]
What is working:
if I remove the tow other objects from $or array which looks like this
[
{
"$lookup":{
"from":"outlets",
"localField":"outlet",
"foreignField":"_id",
"as":"outlets"
}
},
{
"$lookup":{
"from":"suppliers",
"localField":"supplier",
"foreignField":"_id",
"as":"suppliers"
}
},
{
"$match":{
"$and":[
{
"$or":[
{
"suppliers.name":{
"$regex":".*Otilia.*",
"$options":"i"
}
}
]
}
]
}
},
{
"$sort":{
"outlets.name":1,
"_id":1
}
}
]
Note: I used $and because it could be more filters from client-side like purchase order status and more.
Purchase Order document Sample
Supplier document Sample:
outlet document Sample:
The result I want t to achieve is the matched purchase orders documents by the query.
Solved by wrapping every $or expression with {}.
I just forgot to wrap every $or expression in brackets.
the correct format of the query is this:
[
{
"$lookup":{
"from":"outlets",
"localField":"outlet",
"foreignField":"_id",
"as":"outlets"
}
},
{
"$lookup":{
"from":"suppliers",
"localField":"supplier",
"foreignField":"_id",
"as":"suppliers"
}
},
{
"$match":{
"$and":[
{
"$or":[
{
"orderNumber":{
"$regex":".*Celia Pouros.*",
"$options":"i"
}
},
{
"supplierInvoice":{
"$regex":".*Celia Pouros.*",
"$options":"i"
}
},
{
"suppliers.name":{
"$regex":".*Celia Pouros.*",
"$options":"i"
}
}
]
}
]
}
},
{
"$sort":{
"outlets.name":1,
"_id":1
}
}
]

Mongo query for finding out field matching particular array key value

I want to find out elements where type="Normal" and desc==atr.value (atr.value for a particular key atr.key="UDSP"). I am using mongo 3.0.4 so can't use $expr. Below is the dataset :
JSON
"_id":ObjectId("12345"),
"desc":"Foo Bar",
"type":"Normal",
"atr":[
{
"key":"DSP",
"value":"Goo Bar"
},
{
"key":"UDSP",
"value":"Foo Bar"
}
],
"prod":"yes"
}
{
"_id":ObjectId("12347"),
"desc":"Boo Bar",
"type":"Normal",
"atr":[
{
"key":"DSP",
"value":"Goo Bar"
},
{
"key":"UDSP",
"value":"Foo Bar"
}
],
"prod":"yes"
}
It should give result as Ist object only as type="Normal" and atr.value="Foo Bar"(for atr.key="UDSP") and desc="Foo Bar"
You can use this query. First add a field for matched document based on condition and filter based on added field
db.test.aggregate([
{ $match: { type: "Normal", "atr.key":"UDSP" }},
{ $addFields:{
isDocMatched: {
$anyElementTrue: {
$map:{
input: "$atr",
as: "grade",
in: { $eq: [ "$$grade.value", "$desc" ] }
}
}
}
}},
{ $match: { isDocMatched: true } }
])

get the document id and default value from mongo aggregation function

Here is my sample document structure
{
id: "abc"
config: [
{
feature_id: f1,
properties: [
{
id: 1
todo: "check the microwave"
}
{
id: 2
todo: "check the food"
}
]
},
{
feature_id: f2,
}
]
}
I am using mongoDb aggregation function to get todo based on feature_id.
Tried multiple solution but it seems to fails
db.getCollection('foo').aggregate([
{$match: {
"id": "abc",
"config": {
$elemMatch: {
"feature_id": "f1"
}
}
}
},
{
$project : { "config": 1, "_id": 0}
},
{
$unwind: "$configurations"
},
]);
though I am getting the outcome , but it far from satisfactory.
the expected output
[{id: 1 , todo: "check the microwave" }]
You can use use any of the below aggregation query in 3.4.
db.getCollection('foo').aggregate([
{"$match":{"id":"abc","config.feature_id":"f1"}},
{"$unwind":"$config"},
{"$match":{"config.feature_id":"f1"}},
{"$unwind":"$config.properties"},
{"$match":{"config.properties.id":1}},
{"$project":{"data":"$config.properties"}},
{"$replaceRoot":{"newRoot":"$data"}}
])
OR
db.getCollection('foo').aggregate([
{"$match":{"id":"abc","config.feature_id":"f1"}},
{"$project":{
"property":{
"$arrayElemAt":[
{"$filter":{
"input":{
"$let":{
"vars":{
"config":{
"$arrayElemAt":[
{"$filter":{
"input":"$config",
"as":"cf",
"cond":{"$eq":["$$cf.feature_id","f1"]}
}},
0
]
}
},
"in":"$$config.properties"
}
},
"as":"pf",
"cond":{"$eq":["$$pf.id",1]}
}},0]}
}},
{"$replaceRoot":{"newRoot":"$property"}}
])
Here is another solution
db.getCollection('foo').aggregate([
{"$match":{"id":"abc","config.feature_id":"f1"}},
{"$unwind":"$config"},
{"$match":{"config.feature_id":"f1"}},
{"$unwind":"$config.properties"},
{"$match":{"config.properties.id":1}},
{"$project":{"_id": "$config.properties.id", "todo": "$config.properties.todo"}},
])

MongoDb how to count fields

I have some data like this:
{
user_id:1,
group_id:123,
discription:null
},
{
user_id:1,
group_id:321,
discription:null
},
{
user_id:1,
group_id:123,
discription:"text"
},
{
user_id:1,
group_id:321,
discription:"another text"
},
{
user_id:1,
group_id:321,
discription:"another another text"
},
etc..
I want get all groups (group by group_id), count of document in each group, and count of documents in that group that have "discription" with null value and not null value.
So i need results like:
[
[group_id:123, count:2, isNull:1, isNotNull:1],
[group_id:321, count:3, isNull:1, isNotNull:2]
]
I know how to group fields by "group_id" and get the "count", but I don't know how to get the info about "description".
db.collection.aggregate([
{
$match:{
user_id:1
}
},
{
$group:{
_id:'$group_id',
group_id:{$first:'$group_id'},
count:{'$sum':1}
}
}
])
Please check this Query
db.testing.aggregate([{
$match:{
user_id:1
}
},
{$project:{_id:0,user_id:1,group_id: 1,description: { $ifNull: [ "$discription", 1 ] }}},
{
$group:{
_id:'$group_id',
group_count:{'$sum':1},
IsNull:{"$sum":"$description"}
}
},
{$project:{_id:0,group_id:"$_id", count:"$group_count", isNull:"$IsNull", isNotNull:{ $subtract:["$group_count","$IsNull"]}}}
])