Query on 2 properties of the same embedded object in an array - mongodb

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)

Related

How to count the number of documents in which an element of an object inside an array exists in a collection of Mongodb

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.

How to search character by character in mongodb array text field?

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);

How to use arrays as filters by querying subdocuments

I have the following document structure on my mongodb collection:
{
"_id" : "5Ci9sLeBu2iPbWtR5",
"productId" : "010101111",
"description" : "PRODUCT EXAMPLE REF 1001",
"prices" : [
{
"priceId" : 10,
"description" : "Promotions",
"price" : 97.99
},
{
"priceId" : 15,
"description" : "Retail list",
"price" : 105.65
},
{
"priceId" : 20,
"description" : "Standard list",
"price" : 109.10
}
]}
What I want is query only specific array of priceIds, for example: [10,20], resulting:
{
"_id" : "5Ci9sLeBu2iPbWtR5",
"productId" : "010101111",
"description" : "PRODUCT EXAMPLE REF 1001",
"prices" : [
{
"priceId" : 10,
"description" : "Promotions",
"price" : 97.99
},
{
"priceId" : 20,
"description" : "Standard list",
"price" : 109.10
}
]}
Using $in operator with $filter like this (the perfect imaginary scenario):
db.getCollection('products').aggregate([
{$match: { "productId":"010101111" }},
{$project: {
"prices": {
$filter: {
input: "$prices",
as: "price",
cond: { $in: ["$$price.priceId",[10, 20]] }
}
}
}}])
it doesn't work, because mongodb complains about $in operator ("invalid operator '$in'").
Of course, I can do that with $unwind, but I'll have a performance problem, because I need to group again after all.
The closest answers I've found for my question were these two:
Retrieve only the queried element in an object array in MongoDB collection
Filter array using the $in operator in the $project stage
but none of them were about searching with array filter in subdocuments.
The $in operator is only valid within the aggregation pipeline for MongoDB 3.4+. However, a workaround involves using the set operators. Use $setIsSubset as substitute which returns true if all elements of the first set appear in the second set, including when the first set equals the second set:
cond: { $setIsSubset: [["$$price.priceId"],[10, 20]] }

Mongodb accessing documents

I've the following db:
{ "_id" : 1, "results" : [ { "product" : "abc", "score" : 10 }, { "product" : "xyz", "score" : 5 } ] }
{ "_id" : 2, "results" : [ { "product" : "abc", "score" : 8 }, { "product" : "xyz", "score" : 7 } ] }
{ "_id" : 3, "results" : [ { "product" : "abc", "score" : 7 }, { "product" : "xyz", "score" : 8 } ] }
I want to show the first score of each _id, i tried the following:
db.students.find({},{"results.$":1})
But it doesn't seem to work, any advice?
You can take advantage of aggregation pipeline to solve this.
Use $project in conjunction with $arrayElemAt to point to appropriate node index in the array.
So, to extract the documents of the first score, have written below query.
db.students.aggregate([ {$project: { scoredoc:{$arrayElemAt:["$results",0]}} } ]);
In case if you just wish to have scores excluding product, use $results.score as shown below.
db.students.aggregate([ {$project: { scoredoc:{$arrayElemAt:["$results.score",0]}} } ]);
Here scoredoc object will have all documents of first score element.
Hope this helps!
According to above mentioned description please try executing following query in MongoDB shell
db.students.find(
{results:
{$elemMatch:{score:{$exists:true}}}}, {'results.$.score':1}
)
According to MongoDB documentation
The positional $ operator limits the contents of an from the
query results to contain only the first element matching the query
document.
Hence in above mentioned query positional $ operator is used in projection section to retrieve first score of each document.

MongoDb Pivot on key value

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.