Find in All subkey of Object MongoDB - mongodb

I have this Document in mongodb
{
"name": "test2",
"_id": "1502609098801598ffeca615f5d3dd09087c6",
"events": {
"0": {
"delay": "0",
"actionid": "2"
},
"1": {
"delay": "0",
"actionid": "3"
}
}
}
I want to find documents that contain event with specific actionid
i tried something like these but i can't find what i want
db.mycollection.find({ "events.$.actionid":"2" })
db.mycollection.find({ "events.$**.actionid":"2" })
db.mycollection.find({ "events.$": { $elemMatch: { "actionid":"2"} })
attention please: i can't change document structure and mongodb version is 3.0.6

We can take use of the aggregation feature within mongodb and project the object to an array
db.test.aggregate( [ { "$project" : { "events" : { "$objectToArray" : "$events" } } } ] )
after this we can just use the normal array filtering with a $match
db.test.aggregate([
{ "$project":
{ "events": {"$objectToArray": "$events"} } },
{ "$match":
{ "events.v.actionid" : "2" } } ] )
This will output the following:
{
"_id" : "1502609098801598ffeca615f5d3dd09087c6",
"events" : [
{
"k" : "0",
"v" : {
"delay" : "0",
"actionid" : "2"
}
},
{
"k" : "1",
"v" : {
"delay" : "0",
"actionid" : "3"
}
}
]
}
>
So you might want to project the document back to its orignial structure

Related

Querying a multi-nested array in MongoDb 3.4.2

MongoDB Version - 3.4.2
I'm trying to query within the Sitecore Analytics database, trying to retrieve all users that are associated with a given List Id.
The example dataset I have follows the default Sitecore Analytics setup:
"Tags" : {
"Entries" : {
"ContactLists" : {
"Values" : {
"0" : {
"Value" : "{1E2D1AB7-72A0-4FF7-906B-DCDC020B87D2}",
"DateTime" : ISODate("2020-10-23T17:38:13.891Z")
},
"1" : {
"Value" : "{28BECCD3-476B-4B1D-9A75-02E59EF21286}",
"DateTime" : ISODate("2018-04-18T14:22:41.763Z")
},
"2" : {
"Value" : "{2C2BB0C3-483D-490E-B93A-9155BFBBE5DC}",
"DateTime" : ISODate("2018-05-10T14:26:08.494Z")
},
"3" : {
"Value" : "{DBE480F6-E305-4B35-9E6D-CBED64F4E44F}",
"DateTime" : ISODate("2018-10-27T02:41:28.776Z")
},
}
}
}
},
I want to iterate through all the entries within Values without having to specify 0/1/2/3, avoiding the following:
db.getCollection('Contacts').find({"Tags.Entries.ContactLists.Values.1.Value": "{28BECCD3-476B-4B1D-9A75-02E59EF21286}"})
I've tried the following:
db.getCollection('Contacts').find({"Tags.Entries.ContactLists.Values": {$elemMatch : {"Value":"{28BECCD3-476B-4B1D-9A75-02E59EF21286}"}}})
db.getCollection('Contacts').find({'Tags' : {$elemMatch : {$all : ['{28BECCD3-476B-4B1D-9A75-02E59EF21286}']}}})
db.getCollection('Contacts').forEach(function (doc) {
for(var i in doc.Tags.Entries.ContactLists.Values)
{
doc.Tags.Entries.ContactLists.Values[i].Value = "{28BECCD3-476B-4B1D-9A75-02E59EF21286}";
}
})
And a few other variations which I cannot recall now. And none work.
Any ideas if this is possible or on how to do this?
I want the outcome to just show filter out the results showing only the entries containing the matching GUID
Many thanks!
Demo - https://mongoplayground.net/p/upgYxgzPwJQ
It can be done using aggregation pipeline
Use $objectToArray to convert array
Use $filter to filter the array
db.collection.aggregate([
{
$addFields: {
filteredValue: {
$filter: {
input: {
$objectToArray: "$Tags.Entries.ContactLists.Values"
},
as: "val",
cond: {
$eq: [ // filter condition
"$$val.v.Value",
"{28BECCD3-476B-4B1D-9A75-02E59EF21286}"
]
}
}
}
}
}
])
Output -
[
{
"Tags": {
"Entries": {
"ContactLists": {
"Values": {
"0": {
"DateTime": ISODate("2020-10-23T17:38:13.891Z"),
"Value": "{1E2D1AB7-72A0-4FF7-906B-DCDC020B87D2}"
},
"1": {
"DateTime": ISODate("2018-04-18T14:22:41.763Z"),
"Value": "{28BECCD3-476B-4B1D-9A75-02E59EF21286}"
},
"2": {
"DateTime": ISODate("2018-05-10T14:26:08.494Z"),
"Value": "{2C2BB0C3-483D-490E-B93A-9155BFBBE5DC}"
},
"3": {
"DateTime": ISODate("2018-10-27T02:41:28.776Z"),
"Value": "{DBE480F6-E305-4B35-9E6D-CBED64F4E44F}"
}
}
}
}
},
"_id": ObjectId("5a934e000102030405000000"),
"filteredValue": [
{
"k": "1",
"v": {
"DateTime": ISODate("2018-04-18T14:22:41.763Z"),
"Value": "{28BECCD3-476B-4B1D-9A75-02E59EF21286}"
}
}
]
}
]
You can not use $elemMatch because Values is not array, but object. You can solve the problem with Aggregation Pipeline:
$addFields to add new field Values_Array that will be array representation of Values object.
$objectToArray to transform Values object to array
$match to find all documents that has requested value in new Values_Array field
$project to specify which properties to return from the result
db.getCollection('Contacts').aggregate([
{
"$addFields": {
"Values_Array": {
"$objectToArray": "$Tags.Entries.ContactLists.Values"
}
}
},
{
"$match": {
"Values_Array.v.Value": "{28BECCD3-476B-4B1D-9A75-02E59EF21286}"
}
},
{
"$project": {
"Tags": 1
}
}
])
Here is the working example: https://mongoplayground.net/p/2gY-vu3Qrvz

MongoDB group by array subfield

Hello I am new to mongoDB, please I hope you can help me with this question.
My collection will look like this:
{
"_id": { "$oid": "5f1fd47..." },
"email":"c#c.com",
"materials": [
{
"_id": { "$oid": "5f1fda2..." },
"title": "MDF 18mm Blanco",
"id": "mdf18blanco",
"thickness": "18",
"family": "MDF",
"color": ""
}, ...
//others materials with different family
],
}
I did an aggregate like this:
{ "$match" : { "email" : "c#c.com" } },
{ "$unwind" : "$materials" },
{ "$group" : { "_id" : "$_id", "list" : { "$push" : "$materials.family" } } }
and I return this:
{
"_id" : ObjectId("5f1fd47d502e00051c673dd1"),
"list" : [
"MDF",
"MDF",
"MDF",
"Melamina",
"Melamina",
"Melamina",
"Melamina",
"MDF",
"Melamina",
"Aglomerado",
"Aglomerado"
]
}
but i need get this
{
"_id" : ObjectId("5f1fd47d502e00051c673dd1"),
"list" : [
"MDF",
"Melamina",
"Aglomerado"
]
}
I hope you understand my question and can help me, thank you very much.
All you need to do is use $addToSet instead of $push in your group stage:
{ "$group" : { "_id" : "$_id", "list" : { "$addToSet" : "$materials.family" } } }
One thing to note is that $addToSet does not guarantee a specific order as opposed to $push in case it matters to you.
You only need change $push to $addToSet.
A set not contains repeat values so it works.
db.collection.aggregate([
{
"$match": {
"email": "c#c.com"
}
},
{
"$unwind": "$materials"
},
{
"$group": {
"_id": "$_id",
"list": {
"$addToSet": "$materials.family"
}
}
}
])
Mongo Playground example

Querying a multi value array to retrieve specific value in mongodb

I have an array element in my document db with multiple parameters.This is how a single document looks like. I can search based on name which is unique. Is there a way to list all technologies associated with the name.
"name" : "Sam",
"date" : ISODate("2020-02-05T06:34:28.453Z"),
"technology" : [
{
"technologyId" : "1",
"technologyName" : "tech1"
},
{
"technologyId" : "2",
"technologyName" : "tech2"
},
{
"technologyId" : "3",
"technologyName" : "tech3"
},
{
"technologyId" : "4",
"technologyName" : "tech4"
}
],
"sector" : [
{
"sectorId" : "1",
"sectorName" : "sector1"
},
{
"sectorId" : "2",
"sectorName" : "sector2"
},
{
"sectorId" : "3",
"sectorName" : "sector3"
},
{
"sectorId" : "4",
"sectorName" : "sector4"
}
]
This is my simple query
db.getCollection('myCollection').find({'name':'Sam'})
Is there a way to retrieve all technologies for a name in a single query.
My output should have only tech1,tech2,tech3,tech4.
A two stage aggregation using $match, $project and $map.
Query:
db.collection.aggregate([
{
$match: {
name: "Sam"
}
},
{
$project: {
"name": "$name",
"technologies": {
$map: {
input: "$technology",
as: "t",
in: "$$t.technologyName"
}
}
}
}
]);
Result:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"name": "Sam",
"technologies": [
"tech1",
"tech2",
"tech3",
"tech4"
]
}
]
In case you don't want the name in the final O/P remove it from project stage.
I'm considering that you don't have duplicate tech under a single name. You can project only the tech names then map:
db.getCollection('myCollection')
.find({ name: 'Sam' }, { 'technology.technologyName': 1 })
.map(function(doc) { return doc['technology.technologyName'] })

Return null default value if no result found

I have a collection that looks like this:
{
"value" : "20",
"type" : "square",
"name" : "form1"
}
{
"value" : "24",
"type" : "circle",
"name" : "form2"
}
{
"value" : "12",
"type" : "square",
"name" : "form3"
}
I want to extract a document with name = form2 so I type:
db.myCollec.find({"name":"form2"} , {"name":1, "type":1, "_id":0})
The result is:
{ "name" : "form2", "type" : "circle" }
Now if I want to find a document with name = form4 I type:
db.myCollec.find({"name":"form4"} , {"name":1, "type":1, "_id":0})
But this returns nothing because there is no document with this name.
However I want the return value to look like this:
{ "name" : "form4", "type" : null }
How would I do this?
You can use below aggregation
Mongodb doesn't produce the result if there is not $matched data found with the query.
But you can use $facet aggregation which processes multiple aggregation pipeline within single stage.
So first use $facet to get the $matched documents and use $projection if no ($ifNull) data found.
let searchTerm = "form2"
db.myCollec.aggregate([
{ "$facet": {
"data": [
{ "$match": { "name": searchTerm }},
{ "$project": { "name": 1, "type": 1, "_id": 0 }}
]
}},
{ "$project": {
"name": {
"$ifNull": [{ "$arrayElemAt": ["$data.name", 0] }, searchTerm ]
},
"type": {
"$ifNull": [{ "$arrayElemAt": ["$data.type", 0] }, null]
}
}}
])
Why you dont check in callback if result==null and create your own empty object?
let name = "form4";
db.myCollec.find({"name":name} , {"name":1, "type":1, "_id":0}, function(err, result){
if(err) {
// Error handling
return;
}
if (result==null){
result = {"name":name, "type":null};
}
});

How to check keys of key value pair inside mongodb structure

I have a mongodb collection where objects are structured as such:
{
"id": "1234",
"history": [
{
"userid": 100,
"myobjects": [{id, id1, id4}]
},
{
"userid": 200,
"myobjects": [{id2, id3, id5}]
},
}
Goal: if my userid is 100, return an object that doesn't contain my userid in its history. I'm guessing it'd be some kind of "my userid not in keys of history field" but i'm not sure how to write that out. Here's my basic idea:
Collection.findOne(
{
in_progress : null,
history : {"$nin": myuserid } ???
}
);
Any help would be appreciated!
Use MongoDB aggregation.
db.test.aggregate([
{
"$unwind" : "$history"
},
{
"$match" : {
"history.userid" : {
"$ne" : 100
}
}
},
{
"$group" : {
"_id" : "$id",
"history" : {
"$push" : "$history"
}
}
}
]);