Create MongoDb view contain find clause - mongodb

Imagine I have a collection named user with below fields
- name
- family
- age
and I want to create a view that return all users that are 19 and 21

try
db.createView( "viewName", "collection", [
{ $match : { key : value } }
,...
,...
])

OK let's start by adding some data to a users collection, lets have a few people at diffrent ages:
> db.users.insertMany([
... { "name": "foo", age: 12 },
... { "name": "bar", age: 19 },
... { "name": "wibble", age: 20 },
... { "name": "wobble", age: 21 }
... ]);
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("5bd18277289332325f377fff"),
ObjectId("5bd18277289332325f378000"),
ObjectId("5bd18277289332325f378001"),
ObjectId("5bd18277289332325f378002")
]
}
We can then write a view on top of this collection using the createView method which takes in a aggregation pipeline:
> var pipelines = [ { "$match" : { "$or" : [ { "age" : 19 }, { "age" :21 } ] } } ];
> db.createView("users19and21", "users", pipelines);
{ "ok" : 1 }
If you want to test your pipeline out before creating the view you can just call the aggregate method on the users collection, for example:
> var pipelines = [ { "$match" : { "$or" : [ { "age" : 19 }, { "age" :21 } ] } } ];
> db.users.aggregate(pipelines);
{ "_id" : ObjectId("5bd18277289332325f378000"), "name" : "bar", "age" : 19 }
{ "_id" : ObjectId("5bd18277289332325f378002"), "name" : "wobble", "age" : 21 }
Once we have our view we can just query it like a collection:
> db.users19and21.find()
{ "_id" : ObjectId("5bd18277289332325f378000"), "name" : "bar", "age" : 19 }
{ "_id" : ObjectId("5bd18277289332325f378002"), "name" : "wobble", "age" : 21 }
More information on creating views in mongodb can be found on their documentation - https://docs.mongodb.com/manual/reference/method/db.createView/

Related

Spring data mongoDB push operation with multiple fields are not working

I have a set of data objects in MongoDB as-
[
{ "status" : "passed", "version" : "124" , "value" : 6 },
{ "status" : "passed", "version" : "123" , "value" : 10 },
{ "status" : "failed", "version" : "123" , "value" : 16 }
]
and i want to get it in a format like -
[
{
version: 124,
"series" :[
{
"name" : "passed",
"value" : 6
}
]
},
{
version: 123,
"series" : [
{
"name" : "passed",
"value" : 10
},
{
"name" : "failed",
"value" : 16
}
]
}
]
how should i write the query?
I wrote query like
I have written a query like:
Aggregation.group("version").push(new BasicDBObject("name","$status").append("value", "$value")).as("series");
using the aggregate query above, I'm getting like:
[
{
version: 124,
"series" :[
{
"name" : null,
"value" : 6
}
]
},
{
version: 123,
"series" : [
{
"name" : null,
"value" : 10
},
{
"name" : null,
"value" : 16
}
]
}
]
It seems that the value of the status is not taken in the object. How can I resolve this?
My Query is like:
db.results.aggregate([{
{
       "$group": {
             "_id": {
                  "status": "$status",
                  "version": "$version",
              },
             "count": {
                  "$sum": 1
              }
            }
        }, {
            $group: {
                _id: "$_id.version",
                "series": {
                    $push: {
                        "status": "$_id.status",
                        "value": "$count"
                    }
                }
            }
        }
    ]);
So I have to add the aggregation query as:
Aggregation.group("version").push(new BasicDBObject("_id", "$_id.status").append("value", "$value")).as("series");
I tried with mongo aggregation. Try in the shell.
I am not familiarised with spring.
db.getCollection('Test1').aggregate([
{
$group:{
_id:"$version",
version:{$first:"$version"},
series:{
$push:{
name:"$status",
value:"$value"
}
}
}
},
{$project:{_id:0}}
])

Apply $elemMatch to multiple arrays

The result of my query is:
{
"_id" : ObjectId("5b5e680bca55f2885cb3c864"),
"barcelona" : [
{
"date" : ISODate("2018-07-01T00:00:00.000+0000"),
"name" : "david",
"age" : 17.0
}
]
}
My query looks like this:
db.test8.find({ line: '2' },{ barcelona: { $elemMatch: {
date :
{
"$gte" : ISODate("2018-07-01T00:00:00Z"),
"$lt" : ISODate("2018-07-02T00:00:00Z")
}
}}});
The dataset looks like this in the mongodb. It has three arrays: barcelona, paris, and london. I will answer any questions you may have.
{
"_id" : ObjectId("5b5e680bca55f2885cb3c864"),
"piwikID" : "2",
"barcelona" : [
{
"date" : ISODate("2018-07-01T00:00:00.000+0000"),
"name" : "david",
"age" : 11.0
},
{
"date" : ISODate("2018-07-02T00:00:00.000+0000"),
"name" : "damo",
"age" : 16.0
},
{
"date" : ISODate("2018-07-03T00:00:00.000+0000"),
"name" : "dani",
"age" : 12.0
}
],
"paris" : [
{
"date" : ISODate("2018-07-01T00:00:00.000+0000"),
"name" : "david",
"age" : 17.0
},
{
"date" : ISODate("2018-07-02T00:00:00.000+0000"),
"name" : "damo",
"age" : 10.0
},
{
"date" : ISODate("2018-07-03T00:00:00.000+0000"),
"name" : "danp",
"age" : 13.0
}
],
"london" : [
{
"date" : ISODate("2018-07-01T00:00:00.000+0000"),
"name" : "dan",
"age" : 11.0
},
{
"date" : ISODate("2018-07-02T00:00:00.000+0000"),
"name" : "donner",
"age" : 12.0
},
{
"date" : ISODate("2018-07-03T00:00:00.000+0000"),
"name" : "dangus",
"age" : 14.0
}
]
};
There are other cities paris, london, etc. which are arrays and should display in my result.
How can I modify my query to show them in my result as well?
You want to use $elemMatch for multiple arrays first, you need to build the dynamic query.
Suppose you know the city names before you queried the DB you can build a query like this.
var cities = ['barcelona', 'paris', 'london'];
var query = {
"$or": []
};
for (let i = 0; i < cities.length; i++) {
let queryObj = {};
queryObj[cities[i]] = {
"$elemMatch": {
date:
{
"$gte": ISODate("2018-07-01T00:00:00Z"),
"$lt": ISODate("2018-07-02T00:00:00Z")
}
}
};
query["$or"].push(queryObj);
};
Your query like this :
{
"$or":
[{ "barcelona": { "$elemMatch": { "date": { "$gte": ISODate("2018-07-01T00:00:00Z"), "$lt": ISODate("2018-07-02T00:00:00Z") } } } },
{ "paris": { "$elemMatch": { "date": { "$gte": ISODate("2018-07-01T00:00:00Z"), "$lt": ISODate("2018-07-02T00:00:00Z") } } } },
{ "london": { "$elemMatch": { "date": { "$gte": ISODate("2018-07-01T00:00:00Z"), "$lt": ISODate("2018-07-02T00:00:00Z") } } } }]
}
db.col.find(query);

MongoDB: Aggregation Group operation over a dynamic key-value pair

I have a simple document with one field as a key-value pair. I want to just perform a group operation in Aggregation over those keys and add their values. But the keys in the pair are not fixed and can be anything.
Here is a sample document.
{
_id: 349587843,
matchPair: {
3 : 21,
9 : 4,
7 : 32
}
},
{
_id: 349587478,
matchPair: {
7 : 11,
54 : 32,
9 : 7,
2 : 19
}
}
And I want a result something like the following.
{
_id : 3,
count : 21
},
{
_id : 9,
count : 11
},
{
_id : 7,
count : 43
},
{
_id : 54,
count : 32
},
{
_id : 2,
count : 19
}
I have the following query in mind and tried using $unwindoperation but it doesn't work probably because "matchPair" isn't an array and I don't know what to specify for the $sumoperation.
db.MatchPairs.aggregate([
{ "$unwind" : "$matchPair" },
{ "$group" : {
_id: "$matchPair",
count : { $sum : $matchPair }
} }
]);
I could also try Map-Reduce but for that too I need to emit() keys and values by name.
I'm sure there's a simple solution to this but I can't figure it out.
:
You could start by projecting and reshaping the matchPair field with $objectToArray
New in version 3.4.4.
{
$project: {
matchPair: { $objectToArray: '$matchPair' }
}
}
which would give
{
matchPair: [{ k: 3, v: 21 }, { k: 9, v: 4 }, ...]
}
Then $unwind based on matchPair
{
$unwind: '$matchPair'
}
which would give
{
matchPair: { k: 3, v: 21 }
}
Then $project
{
$project: {
_id: '$matchPair.k',
count: '$matchPair.v'
}
}
That should give the output you want. Altogether would be
.aggregate([
{
$project: {
matchPair: { $objectToArray: '$matchPair' }
}
},
{ $unwind: '$matchPair' },
{
$project: {
_id: '$matchPair.k',
count: '$matchPair.v'
}
}
])
In the mongoDb documentation for $unwind:
Deconstructs an array field from the input documents to output a
document for each element.
So you have to change your schema for something like:
{
"_id" : ObjectId("5880b57b039a3c89c1db145a"),
"matchPair" : [
{
"_id" : "3",
"count" : 21
},
{
"_id" : "9",
"count" : 4
},
{
"_id" : "7",
"count" : 32
}
]
},
{
"_id" : ObjectId("5880b58c039a3c89c1db145b"),
"matchPair" : [
{
"_id" : "7",
"count" : 11
},
{
"_id" : "54",
"count" : 32
},
{
"_id" : "9",
"count" : 7
},
{
"_id" : "2",
"count" : 19
}
]
}
Then doing:
db.MatchPairs.aggregate([
{ $unwind : "$matchPair" }
]);
will return:
{
"_id" : ObjectId("5880b57b039a3c89c1db145a"),
"matchPair" : {
"_id" : "3",
"count" : 21
}
},
{
"_id" : ObjectId("5880b57b039a3c89c1db145a"),
"matchPair" : {
"_id" : "9",
"count" : 4
}
},
{
"_id" : ObjectId("5880b57b039a3c89c1db145a"),
"matchPair" : {
"_id" : "7",
"count" : 32
}
},
{
"_id" : ObjectId("5880b58c039a3c89c1db145b"),
"matchPair" : {
"_id" : "7",
"count" : 11
}
},
{
"_id" : ObjectId("5880b58c039a3c89c1db145b"),
"matchPair" : {
"_id" : "54",
"count" : 32
}
},
{
"_id" : ObjectId("5880b58c039a3c89c1db145b"),
"matchPair" : {
"_id" : "9",
"count" : 7
}
},
{
"_id" : ObjectId("5880b58c039a3c89c1db145b"),
"matchPair" : {
"_id" : "2",
"count" : 19
}
}
Then you just have to do your grouping.

aggregation and display details in mongodb

I have been learning MongoDB, while doing so I tried to implement the aggregation property for my database collection. I grouped the details of the employee based on their age and by using match function, my question is it possible to display the other key-value once they pass the age criteria?
db.employee.aggregate([
{ $match: { age: { $gte: 23 } } },
{
$group: {
_id:'$age',
total: { $sum: 1 },
name: { $addToSet: '$name' }
}
}
])
and the output was like this
{ "_id" : 27, "total" : 2, "name" : [ "indhu", "logesh" ] }
{ "_id" : 26, "total" : 1, "name" : [ "keerthana" ] }
{ "_id" : 25, "total" : 1, "name" : [ "sneha" ] }
{ "_id" : 24, "total" : 1, "name" : [ "dhiva" ] }
{ "_id" : 23, "total" : 1, "name" : [ "elango" ] }
where _id denotes their age.

Aggregate of different subtypes in document of a collection

abstract document in collection md given:
{
vals : [{
uid : string,
val : string|array
}]
}
the following, partially correct aggregation is given:
db.md.aggregate(
{ $unwind : "$vals" },
{ $match : { "vals.uid" : { $in : ["x", "y"] } } },
{
$group : {
_id : { uid : "$vals.uid" },
vals : { $addToSet : "$vals.val" }
}
}
);
that may lead to the following result:
"result" : [
{
"_id" : {
"uid" : "x"
},
"vals" : [
[
"24ad52bc-c414-4349-8f3a-24fd5520428e",
"e29dec2f-57d2-43dc-818a-1a6a9ec1cc64"
],
[
"5879b7a4-b564-433e-9a3e-49998dd60b67",
"24ad52bc-c414-4349-8f3a-24fd5520428e"
]
]
},
{
"_id" : {
"uid" : "y"
},
"vals" : [
"0da5fcaa-8d7e-428b-8a84-77c375acea2b",
"1721cc92-c4ee-4a19-9b2f-8247aa53cfe1",
"5ac71a9e-70bd-49d7-a596-d317b17e4491"
]
}
]
as x is the result aggregated on documents containing an array rather than a string, the vals in the result is an array of arrays. what i look for in this case is to have a flattened array (like the result for y).
for me it seems like that what i want to achieve by one aggegration call only, is currently not supported by any given operation as e.g. a type conversion cannot be done or unwind expectes in every case an array as input type.
is map reduce the only option i have? if not ... any hints?
thanks!
You can use the aggregation to do the computation you want without changing your schema (though you might consider changing your schema simply to make queries and aggregations of this field easier to write).
I broke up the pipeline into multiple steps for readability. I also simplified your document slightly, again for readability.
Sample input:
> db.md.find().pretty()
{
"_id" : ObjectId("512f65c6a31a92aae2a214a3"),
"uid" : "x",
"val" : "string"
}
{
"_id" : ObjectId("512f65c6a31a92aae2a214a4"),
"uid" : "x",
"val" : "string"
}
{
"_id" : ObjectId("512f65c6a31a92aae2a214a5"),
"uid" : "y",
"val" : "string2"
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a6"),
"uid" : "y",
"val" : [
"string3",
"string4"
]
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a7"),
"uid" : "z",
"val" : [
"string"
]
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a8"),
"uid" : "y",
"val" : [
"string1",
"string2"
]
}
Pipeline stages:
> project1 = {
"$project" : {
"uid" : 1,
"val" : 1,
"isArray" : {
"$cond" : [
{
"$eq" : [
"$val.0",
[ ]
]
},
true,
false
]
}
}
}
> project2 = {
"$project" : {
"uid" : 1,
"valA" : {
"$cond" : [
"$isArray",
"$val",
[
null
]
]
},
"valS" : {
"$cond" : [
"$isArray",
null,
"$val"
]
},
"isArray" : 1
}
}
> unwind = { "$unwind" : "$valA" }
> project3 = {
"$project" : {
"_id" : 0,
"uid" : 1,
"val" : {
"$cond" : [
"$isArray",
"$valA",
"$valS"
]
}
}
}
Final aggregation:
> db.md.aggregate(project1, project2, unwind, project3, group)
{
"result" : [
{
"_id" : "z",
"vals" : [
"string"
]
},
{
"_id" : "y",
"vals" : [
"string1",
"string4",
"string3",
"string2"
]
},
{
"_id" : "x",
"vals" : [
"string"
]
}
],
"ok" : 1
}
If you modify your schema using always "vals.val" field as an array field (even when the record contains only one element) you can do it easily as follows:
db.test_col.insert({
vals : [
{
uid : "uuid1",
val : ["value1"]
},
{
uid : "uuid2",
val : ["value2", "value3"]
}]
});
db.test_col.insert(
{
vals : [{
uid : "uuid2",
val : ["value4", "value5"]
}]
});
Using this approach you only need to use two $unwind operations: one unwinds the "parent" array and the second unwinds every "vals.val" value. So, querying like
db.test_col.aggregate(
{ $unwind : "$vals" },
{ $unwind : "$vals.val" },
{
$group : {
_id : { uid : "$vals.uid" },
vals : { $addToSet : "$vals.val" }
}
}
);
You can obtain your expected value:
{
"result" : [
{
"_id" : {
"uid" : "uuid2"
},
"vals" : [
"value5",
"value4",
"value3",
"value2"
]
},
{
"_id" : {
"uid" : "uuid1"
},
"vals" : [
"value1"
]
}
],
"ok" : 1
}
And no, you can't execute this query using your current schema, since $unwind fails when the field isn't an array field.