I am new bee in MongoDB, I have collection with key value pairs like below..
input collection
{"restaurantid" : NumberInt("1"),
"Properties" : [
{ "Key" : "A", "Value" : NumberInt("25") },
{ "Key" : "B", "Value" : "StringValue" },
{ "Key" : "C", "Value" : ISODate("2017-02-09") }
] }
I am looking result set as
Output Collection
{ "restaurantid" : NumberInt("1"),
"A" : NumberInt("25"),
"B" : "StringValue",
"C" : ISODate("2017-02-09")
}
How do I get it without hardcoding "A", "B", "C" in the aggregation pipeline. My key value pairs are going to get bigger and is variable for given id
how do I get it without hardcoding “A”, “B”, “C” in the aggregation pipeline. My key value pairs are going to get bigger and is variable for given id
You can utilise new aggregation operator $arrayToObject (SERVER-18794) to pivot MongoDB keys. This operator currently is available in MongoDB v3.4.4+
For example, you can restructure your schema:
{
"restaurantid" : NumberInt("1"),
"Properties" : [
{ "k" : "A", "v" : NumberInt("25") },
{ "k" : "B", "v" : "StringValue" },
{ "k" : "C", "v" : ISODate("2017-02-09") }
]
}
Then you can utilise example aggregation pipeline below:
db.collection.aggregate(
[
{$project:{"tmp":{$arrayToObject:"$Properties"}, "restaurantid":"$resturantid"}},
{$addFields:{"tmp.restaurantid":"$restaurantid"}},
{$replaceRoot:{newRoot:"$tmp"}}
]);
See also $replaceRoot and $addFields. Depending on your use case, you could also take advantage of MongoDB flexible schema and reconsider your document model.
Related
I have a MongoDB collection called Cards containing structurally similar documents as illustrated below:
{
"_id" : ObjectId("mongoid"),
"userName": "Har109",
"array1" : [
{
"id" : "11",
"name" : "hello world",
}
{
"id" : "21",
"name" : "bye world",
}
],
"array2" : [
{
"id" : "1",
"name" : "titanic",
}
{
"id" : "2",
"name" : "avatar",
}
],
"array3" : [
{
"id" : "1",
"name" : "The Alchemist",
}
{
"id" : "2",
"name" : "What is Zero",
}
]
}
I need to query: Count the number of Documents in the collection that matches "array1.id", which I then can do it for all other arrays (e.g. "array2.id", "array3.id")
What is the syntax for this kind of query in MongoDB Compass?
I tried: the pipeline to get all the documents where there is array1
[{$count: 'array1'}]
But how do I add the condition of where array1.id = "11" to this pipeline on mongodb compass, where stages are done separately.
Count the number of Documents in the collection that matches "array1.id"
To find how many documents contain array.id=x you can run:
db.collection.find({ "array1.id":"11" }).countDocuments()
Or using aggregation
db.collection.aggregate([
{ $match:{"array1.id":"11"} },
{ count:"array1" }
])
The algorithm traverses arrays automatically.
It won't find a document if the key does not exist.
I have documents in mongodb are like :
[{
"_id" : 1,
"name" : "Himanshu",
"tags" : ["member", "active"]
},{
"_id" : 2,
"name" : "Teotia",
"tags" : ["employer", "withdrawal"]
},{
"_id" : 3,
"name" : "John",
"tags" : ["member", "deactive"]
},{
"_id" : 4,
"name" : "Haris",
"tags" : ["employer", "action"]
}]
What I want to search here is if we have array of filter like {"tags" : ["member", "act"]} it will reply back id's 1 and 2 because here member is full match and act partial match in two documents.
if I have filter like {"tags" : ["mem"] } then it should reply id's 1 and 3
One more case If I have filter like {"tags" : ["member", "active"]} then it should answer only 1.
You basically need two concepts here.
Convert each input string of the array into an Regular expression anchored to the "start" of the string:
Apply the list with the $all operator to ensure "all" matches:
var filter = { "tags": [ "mem", "active" ] };
// Map with regular expressions
filter.tags = { "$all": filter.tags.map(t => new RegExpr("^" + t)) };
// makes filter to { "tags": { "$all": [ /^mem/, /^active/ ] } }
// search for results
db.collection.find(filter);
If I have a schema similar to the following:
{
"_id" : "12345",
"_class" : "Refrigerator",
"items" : {
"APPLE" : {
"id" : 123,
"name" : "APPLE"
},
"BANANA" : {
"id" : 234,
"name" : "BANANA"
},
"STRAWBERRY" : {
"id" : 345,
"name" : "STRAWBERRY"
}
},
},
{
"_id" : "23456",
"_class" : "Pantry",
"items" : {
"CEREAL" : {
"id" : 456,
"name" : "CEREAL"
}
},
}
I want to get a list of distinct items where the _class=Refrigerator. The result should be ["APPLE","BANANA","STRAWBERRY"].
How can I do this? Thank you!
You can utilise aggregation operator $objectToArray (SERVER-23310) to turn keys into values. It should be able to group 'unknown' or 'dynamic' fields. This operator is available in MongoDB v3.4.4+
Given your example documents, you can use below aggregation pipeline
db.collection.aggregate([
{$match:{"class":"Refrigerator"}},
{$project:{"items":{$objectToArray:"$items"}}},
{$unwind:"$items"},
{$group:{_id:"$_id", result:{$addToSet:"$items.k"}}}
])
See also the manual for: $unwind, $group, and $addToSet
If applicable for your use case, I would recommend to re-consider your data schema for easier access to the information that you're after. MongoDB has a flexible schema, you should use this for your application benefits.
I have a Collection of objects :
db.coll.find().pretty();
{
"_id" : "1",
"elements" : [
{ "key" : "A", "value" : 10 },
{ "key" : "B", "value" : 1 },
]
},
{
"_id" : "2",
"elements" : [
{ "key" : "A", "value" : 1 },
{ "key" : "C", "value" : 33 },
]
}
I want to find the documents that contain an element with "key" equal to "A" AND its "value" is greater than 5.
Not sure if this is possible without using the aggregation framework.
Without aggregation, using $elemMatch and query will be as below :
db.coll.find({"elements":{"$elemMatch":{"$and":[{"key":"A"},{"value":{"$gt":5}}]}}}).pretty()
or if you want use aggregation then used following query for aggregation
db.coll.aggregate({"$unwind":"$elements"},{"$match":{"$and":[{"elements.key":"A"},{"elements.value":{"$gt":5}}]}}).pretty()
Here is the query
db.coll.find({elements: {$elemMatch: {key: "A", value: {$gt: 5}}}});
It uses $elemMatch operator. The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria. (Refer the documentation)
I have a collection:
{
"_id" : ObjectId("5338ec2a5b5b71242a1c911c"),
"people" : [
{
"name" : "Vasya"
},
{
"age" : "30"
},
{
"weight" : "80"
}
],
"animals" : [
{
"dog" : "Sharick"
},
{
"cat" : "Barsik"
},
{
"bird" : "parrot"
}
]},{
"_id" : ObjectId("5338ec7f5b5b71242a1c911d"),
"people" : [
{
"name" : "Max"
},
{
"age" : "32"
},
{
"weight" : "78"
}
],
"animals" : [
{
"dog" : "Borbos"
},
{
"cat" : "Murka"
},
{
"bird" : "Eagle"
}
]}
then I combine two arrays "people" and "animals"
db.tmp.aggregate({$project:{"union":{$setUnion:["$people","$animals"]}}})
in the issue:
How to make the fields of each record array "result" to be displayed in a single order, and not randomly?
that is:
Wish I could find the quote ( If I can I will add it ), but it basically comes from the CTO of MongoDB and is essentially (sic) "Set's are not considered to be ordered". And very much so from a Math point of view that is true.
So you have stumbled upon one of the new features in the current (as of writing) 2.6 release candidate series. But like with it's $addToSet counterpart, the resulting set from $setUnion will not be sorted in any way.
To do this you need to $unwind and $sort and then $group again using $push, just as you always have with $addToSet. And of course you would need some common key in order to $sort on this, which your data does not.
Update: Here is the quote, and here is another.