I have the following document which is also available in the mongo playground at:
https://mongoplayground.net/p/zhcoi1BF0Ny
db={
MyCollectionOne: [
{
"firstId": "10",
"secondId": "123456789012345678901234"
},
{
"firstId": "11",
"secondId": "999999999999999999999999"
}
],
MyCollectionTwo: [
{
"_id": ObjectId("123456789012345678901234"),
"otherFieldOne": "Some Data",
"otherFieldTwo": [
{
someNumber: 7
}
]
},
{
"_id": ObjectId("999999999999999999999999"),
"otherFieldOne": "Some Other Data",
"otherFieldTwo": [
{
someNumber: 9
},
{
someNumber: 39
}
]
}
]
}
Given a firstId, I need to determine the correct MyCollectionTwo entry to return. So for example, if I was given a firstId of 11, I would use that to lookup its corresponding secondId (which is "999999999999999999999999"). I would need to convert the secondId value to an ObjectId and then look through the MyCollectionTwo _id fields until I find the matching one.
I gave it a try and am very close but cannot figure out how to correctly do the string->objectId conversion.
db.MyCollectionTwo.aggregate([
{
$lookup: {
from: "MyCollectionOne",
localField: "_id",
foreignField: "secondId",
as: "Temp"
}
},
{
$unwind: "$Temp"
},
{
$match: {
"Temp.firstId": "11"
}
},
{
$project: {
_id: 1,
otherFieldOne: 1,
otherFieldTwo: 1
}
}
]).find()
I am aware there is a let/pipeline which can use a $toObjectId but I can't get that working in the above context.
Any help would be appreciated. Thanks!
Your $lookup with pipeline should be as below:
$lookup: {
from: "MyCollectionOne",
let: {
id: "$_id"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
{
$toObjectId: "$secondId"
},
"$$id"
]
}
}
}
],
as: "Temp"
}
Sample Mongo Playground
You can try this
db.MyCollectionTwo.aggregate([
{
$lookup: {
//searching collection name
from: "MyCollectionOne",
//setting variable [searchId] where your string converted to ObjectId
let: {
"searchId": {
$toObjectId: "$secondId"
}
},
//search query with our [searchId] value
"pipeline": [
//searching [searchId] value equals your field [_id]
{
"$match": {
"$expr": [
{
"_id": "$$searchId"
}
]
}
},
],
as: "Temp"
}
},
{
$unwind: "$Temp"
},
{
$match: {
"Temp.firstId": "11"
}
},
{
$project: {
_id: 1,
otherFieldOne: 1,
otherFieldTwo: 1
}
}
]).find()
https://mongoplayground.net/p/es0j0AiPDCj
Related
So I’m trying to aggregate two documents matched on an id and based on the value of the first.
Document 1
{
“id”:3
“Whats for dinner”: “dinner”,
“What is for dinner tonight”: “dinner”,
“Whats for lunch”:“lunch”
}
Document 2
{
“Id”:3
“dinner” : “We are having roast!”,
“lunch” : “We are having sandwiches”
}
I’d like to start by matching the id and test if the question exists in doc1.
then return the question from doc1 and the answer from doc 2 . Like
{“Whats for dinner”:“We are having roast!”}
I’ve tried:
{ “$match”: { “id”: 3, “Whats for dinner”:{"$exists":True}} },
{
"$lookup": {
"from": "doc 2",
"localField": "id",
"foreignField": "id",
"as": "qa"
}
}
But from here I can’t figure out how to use the value from doc1 as key in doc2
It might be simple! but I’m a new to this, and just can’t get it to work!?
Crazy data model! This would be a solution:
db.doc1.aggregate([
{ $project: { data: { $objectToArray: "$$ROOT" } } },
{ $unwind: "$data" },
{
$lookup: {
from: "doc2",
pipeline: [
{ $project: { data: { $objectToArray: "$$ROOT" } } }
],
as: "answers"
}
},
{
$set: {
answers: {
$first: {
$filter: {
input: { $first: "$answers.data" },
cond: { $eq: [ "$$this.k", "$data.v" ] }
}
}
}
}
},
{ $match: { answers: { $exists: true } } },
{
$project: {
data: [
{
k: "$data.k",
v: "$answers.v"
}
]
}
},
{ $replaceWith: { $arrayToObject: "$data" } }
])
Mongo Playground
Better don't use any user data as key names, you will always have to juggle with $objectToArray and $arrayToObject
Maybe consider this:
questions: {
guildid: 3,
text: [
"Whats for dinner",
"What is for dinner tonight",
"Whats for lunch"
],
"nospace": 1
}
I have two collections, viz: clib and mp.
The schema for clib is : {name: String, type: Number} and that for mp is: {clibId: String}.
Sample Document for clib:
{_id: ObjectId("6178008397be0747443a2a92"), name: "c1", type: 1}
{_id: ObjectId("6178008397be0747443a2a91"), name: "c2", type: 0}
Sample Document for mp:
{clibId: "6178008397be0747443a2a92"}
{clibId:"6178008397be0747443a2a91"}
While Querying mp, I want those clibId's that have type = 0 in clib collection.
Any ideas how this can be achieved?
One approach that I can think of was to use $lookUp, but that doesnt seem to be working. Also, I m not sure if this is anti-pattern for mongodb, another approach is to copy the type from clib to mp while saving mp document.
If I've understood correctly you can use a pipeline like this:
This query get the values from clib where its _id is the same as clibId and also has type = 0. Also I've added a $match stage to not output values where there is not any coincidence.
db.mp.aggregate([
{
"$lookup": {
"from": "clib",
"let": {
"id": "$clibId"
},
"pipeline": [
{
"$match": {
"$expr": {
"$and": [
{
"$eq": [
{
"$toObjectId": "$$id"
},
"$_id"
]
},
{
"$eq": [
"$type",
0
]
}
]
}
}
}
],
"as": "result"
}
},
{
"$match": {
"result": {
"$ne": []
}
}
}
])
Example here
db.mp.aggregate([
{
$lookup: {
from: "clib",
let: {
clibId: "$clibId"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [ "$_id", "$$clibId" ],
}
]
}
}
},
{
$project: { type: 1, _id: 0 }
}
],
as: "clib"
}
},
{
"$unwind": "$clib"
},
{
"$match": {
"clib.type": 0
}
}
])
Test Here
I have a class model which has field ref.
I'm trying to fetch only records that match the condition in lookup.
so what i did:
{
$lookup: {
from: 'fields',
localField: "field",
foreignField: "_id",
as: 'FieldCollege',
},
},
{
$addFields: {
"FieldCollege": {
$arrayElemAt: [
{
$filter: {
input: "$FieldCollege",
as: "field",
cond: {
$eq: ["$$field.level", req.query.level]
}
}
}, 0
]
}
}
},
The above code works fine and returning the FieldCollege if the cond is matched.
but the thing is, i wanted to return the class records only if the FieldCollege is not empty.
I'm totally new to mongodb. so i tried something like this:
{
$match: {
'FieldCollege': { $exists: true, $ne: [] }
}
},
Obv this didn't work.
does mongodb support something like this or am i complicating things?
EDIT:
the result from the above code:
"Classes": [
{
"_id": "613245664c6ea614e001fcef",
"name": "test",
"language": "en",
"year_cost": "3232323",
"FieldCollege":[] // with $unwind
}
],
expected Result:
"Classes": [
// FieldCollege is empty
],
I think the good option is to use lookup with pipeline, and see the final version of your query,
$lookup with fields collection and match your both conditions
$limit to result one document
$match FieldCollege is not empty []
$addElemAt to get first element from result FieldCollege
[
{
$lookup: {
from: "fields",
let: { field: "$field" },
pipeline: [
{
$match: {
$and: [
{ $expr: { $eq: ["$$field", "$_id"] } },
{ level: req.query.level }
]
}
},
{ $limit: 1 }
],
as: "FieldCollege"
}
},
{ $match: { FieldCollege: { $ne: [] } } },
{
$addFields: {
FieldCollege: { $arrayElemAt: ["$FieldCollege", 0] }
}
}
]
I am grabbing an id from a nested value in a schema, then using that to lookup the id from another table and also matching on a couple dates from that table. I've tried a regular match/lookup/unwind/match and also a lookup/let/pipeline technique. In both cases, it ignores matching on the date for some reason. What am I missing?
Here is one method for reference. I'm not sure where to put the sort either since it doesn't seem to pull $meeting out to sort on.
EXAMPLE RECORDS
PRODUCT
{
"_id" : ObjectId("5f36c0df6d5553e6af208cac"),
"items" : [
{
"paramType" : "Meeting",
"paramValue" : "5f36c0df6d5553e6af208cab"
}
],
"ownerId" : ObjectId("12345678901234567")
}
MEETING
{
"_id" : ObjectId("5f36c0df6d5553e6af208cab"),
"startDate" : ISODate("2020-08-18T10:00:00.000+0000"),
"endDate" : ISODate("2020-08-18T11:00:00.000+0000")
}
AGGREGATE
db.getCollection("products").aggregate(
[
{
$match: {
"ownerId": ObjectId("12345678901234567")
}
},
{
$unwind: "$items"
},
{
$lookup: {
from: "meetings",
let: { "meetingId": '$items.paramValue' },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$_id", "$$meetingId"] },
{
$eq: ["meeting.startDate", {
"$gte": ["$meeting.startDate", ISODate("2020-08-01T00:00:00.000Z")]
}]
},
{
$eq: ["meeting.endDate", {
"$lte": ["$meeting.endDate", ISODate("2020-08-31T23:59:59.999Z")]
}]
}
],
},
},
},
],
as: "meeting"
}
},
{
$unwind: "$meeting"
},
{
$project: {
"_id": 1,
"items": 1,
"meeting": "$meeting"
}
},
{
$sort: {
'meeting.startDate': 1
}
},
]
);
It might be because item.paramValue is not converted to an ObjectId before the lookup. But can't figure out how to convert it inside an aggregate. I tried this, but no go
{
$addFields: {
"convertedMeetingId": { $toObjectId: "$items.paramValue" }
}}
let: { "meetingId": "$convertedMeetingId" }
There are quick fixes in $lookup other looks good,
let you can convert meetingId string to ObjectId here using $toObjectId
$gte and $lte, you used $meeting.startDate and $meeting.endDate it should be $startDate and $endDate because you are already inside meeting lookup.
i am not sure why you have used $eq and with $gte and $lte, if i am not wrong i have corrected and removed $eq it will work directly.
{
$lookup: {
from: "meetings",
let: { meetingId: { $toObjectId: "$items.paramValue" } },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$_id", "$$meetingId"] },
{ $gte: ["$startDate", ISODate("2020-08-01T00:00:00.000Z")] },
{ $lte: ["$endDate", ISODate("2020-08-31T23:59:59.999Z")] }
]
}
}
}
],
as: "meeting"
}
},
Playground
I am working with a mongodb aggregation and I would need to replace the part of my object array to an array concatenation. I understand that the part that I have to modify would be push but it is added as objects and I am not very clear how to concatenate that part
This is my current aggregation:
Datagreenhouse.aggregate([
{ "$match": { "id_sensor_station_absolute": { "$in": arr }, "recvTime": { $gte: fechainicio, $lte: fechafin } } }, //, "id_station": { "$in": id_station }
{
"$lookup": {
"from": "station_types",
"localField": "id_station", // local field in measurements collection
"foreignField": "id_station", //foreign field from sensors collection
"as": "sensor"
}
},
{ "$unwind": "$sensor" },
{
"$addFields": {
"sensor.attrValue": "$attrValue", // Add attrValue to the sensors
"sensor.recvTime": "$recvTime", // Add attrName to the sensors
"sensor.marca": "$sensor.marca",
"sensor.sensor_type": {
$filter: {
input: '$sensor.sensor_type',
as: 'shape',
cond: { $eq: ['$$shape.name', '$attrName'] },
}
}
}
},
{
"$group": {
"_id": {
"name_comun": "$sensor.sensor_type.name_comun",
"name_comun2": "$sensor.marca"
}, // Group by time
"data": {
"$push": {
"name": "$sensor.recvTime",
"value": "$sensor.attrValue",
},
},
}
},
{
"$project": {
"name": {
$reduce: {
input: [
["$_id.name_comun2"]
],
initialValue: "$_id.name_comun",
in: { $concatArrays: ["$$value", "$$this"] }
}
},
"_id": 0,
"data": 1
}
}
]
The current output of this aggregation is:
[
{
"data":[
{
"name":"2020-06-04T14:30:50.00Z",
"value":69.4
},
{
"name":"2020-06-04T14:13:31.00Z",
"value":68.9
}
],
"name":[
"Hum. Relativa",
"Hortisys"
]
},
{
"data":[
{
"name":"2020-06-04T14:30:50.00Z",
"value":25.5
},
{
"name":"2020-06-04T14:13:31.00Z",
"value":26.6
}
],
"name":[
"Temp. Ambiente",
"Hortisys"
]
}
]
I need to change the format of data for data: [[], [], []]
Example:
[
{
"data":[
["2020-06-04T14:30:50.00Z",69.4],
["2020-06-04T14:13:31.00Z",68.9],
"name":[
"Hum. Relativa",
"Hortisys"
]
},
{
"data":[
["2020-06-04T14:30:50.00Z",69.4],
["2020-06-04T14:13:31.00Z",68.9],
"name":[
"Temp. Ambiente",
"Hortisys"
]
}
]
The $push operator won't accept an array directly, but you can give it another operator that returns an array, like
data:{ $push:{ $concatArrays:[[ "$sensor.recvTime", "$sensor.attrValue" ]]}},