Query MongoDB subfield query select/find? not sure what to do - mongodb

I have a dataset that looks like this:
{ "_id" : ObjectId( "----number-----" ),
"interaction" : { "author" : { "link" : "------",
"avatar" : "----link---",
"name" : "-----name----",
"id" : "12345678" },
How do I query mongodb to give me a list of id's from this sort of field? If you know, I also need to group them descending by count. Is there an equivalent of group by as in sql?
Thanks!

Look into the MongoDB Aggregation Framework.
The query you're looking for should be like this.
db.collection.aggregate([{$group: {_id: '$iteraction.id', hits: {$sum: 1}}}, {$sort: {hits: -1}}])

Related

MongoDB get all embedded documents where condition is met

I did this in my mongodb:
db.teams.insert({name:"Alpha team",employees:[{name:"john"},{name:"david"}]});
db.teams.insert({name:"True team",employees:[{name:"oliver"},{name:"sam"}]});
db.teams.insert({name:"Blue team",employees:[{name:"jane"},{name:"raji"}]});
db.teams.find({"employees.name":/.*o.*/});
But what I got was:
{ "_id" : ObjectId("5ddf3ca83c182cc5354a15dd"), "name" : "Alpha team", "employees" : [ { "name" : "john" }, { "name" : "david" } ] }
{ "_id" : ObjectId("5ddf3ca93c182cc5354a15de"), "name" : "True team", "employees" : [ { "name" : "oliver" }, { "name" : "sam" } ] }
But what I really want is
[{"name":"john"},{"name":"oliver"}]
I'm having a hard time finding examples of this without using some kind of programmatic iterator/loop. Or examples I find return the parent document, which means I'd have to parse out the embedded array employees and do some kind of UNION statement?
Eg.
How to get embedded document in mongodb?
Retrieve only the queried element in an object array in MongoDB collection
Can someone point me in the right direction?
Please add projections to filter out the fields you don't need. Please refer the project link mongodb projections
Your find query should be constructed with the projection parameters like below:
db.teams.find({"employees.name":/.*o.*/}, {_id:0, "employees.name": 1});
This will return you:
[{"name":"john"},{"name":"oliver"}]
Can be solved with a simple aggregation pipeline.
db.teams.aggregate([
{$unwind : "$employees"},
{$match : {"employees.name":/.*o.*/}},
])
EDIT:
OP Wants to skip the parent fields. Modified query:
db.teams.aggregate([
{$unwind : "$employees"},
{$match : {"employees.name":/.*o.*/}},
{$project : {"name":"$employees.name",_id:0}}
])
Output:
{ "name" : "john" }
{ "name" : "oliver" }

Mongo Query: how to $lookup with DBRef

I have a trouble with $lookup with DBRef. I couldn't find the solution for below scenario anywhere. Someone please help me here?
Suppose the Collection A is
{
"_id" : ObjectId("582abcd85d2dfa67f44127e0"),
"status" : NumberInt(1),
"seq" : NumberInt(0) }
and Collection B:
{
"_id" : ObjectId("582abcd85d2dfa67f44127e1"),
"Name" : "from B Collection"
"bid" : DBRef("B", ObjectId("582abcd85d2dfa67f44127e0")) }
I have spent lot of time in aggregating above two collections. I am looking for the output as below.
{
"_id" : ObjectId("582abcd85d2dfa67f44127e0"),
"status" : NumberInt(1),
"seq" : NumberInt(0),
B: [
{
"_id" : ObjectId("582abcd85d2dfa67f44127e1"),
"Name" : "from B Collection"
}
]}
Please help me with the Mongo query to retrieve the result in the above format. Thanks in advance
Ideally you would be able to change the DBRef to a plain objectId or just string type. As noted in this post, it can be convoluted to use a DBRef in a lookup. The key is an $addFields stage with {$objectToArray: "$$ROOT.bid"} to get the DBRef value into a usable format.
You'll need to start the aggregation from collection B since that is where the reference is -- and that DBRef needs massaging before doing the lookup. Knowing that is the case, maybe the goal output shape might change; however, here is an aggregation that works to get you what you need:
db.getCollection('B').aggregate([
{$addFields: {fk: {$objectToArray: "$$ROOT.bid"}}},
{$lookup: {
from: 'A',
localField: 'fk.1.v',
foreignField: '_id',
as: 'A'
}},
// the below is transforming data into the format in the example
{$addFields: {'A.B': {_id: '$_id', Name: '$Name'}}},
{$unwind: '$A'},
{$replaceRoot: {newRoot: '$A'}}
])
You might need to do a groupBy if there are multiple B matches you need to group into an array.

I am getting error while running query of add in mongo db , in database collection rohit1 on an keys "a_image" and "b_image"

db.rohit1.find({{$add:["a_image","b_image"]}}:{gt:10})
E QUERY [thread1] SyntaxError: invalid property id #(shell):1:16
You can not use $add with find in the mongodb. Instead i will suggest you to use aggregate.
First you need to create another field which is the sum of two fields a_image and b_image using $project.And, then you need to use $match to check your condition.
try this:
db.rohit1.aggregate([{
$project :{
add : {$add : ["$a_image","$b_image"]},
field1 : "$field1"
... //include all the fields you want to have in the result
}
},{
$match : {add : {$gt : 10}}
}]);
For more information on $project and $match, please read linked mongodb documentation.
EDIT:
you can use $pow inside $add in the same projection.
Try this code:
db.rohit1.aggregate([{
$project :{
add : {$add : [{ $pow : ["$a_image",2]},{$pow : ["$b_image",2]}]},
field1 : "$field1"
... //include all the fields you want to have in the result
}
},{
$match : {add : {$gt : 10}}
}]);

Is it possible to build a map in a MongoDB aggregation?

I'm trying to build a map as part of a group operation in MongoDB but am having no luck.
My documents are structured as follows :
type : artist,
artist : David Bowie,
song : "Starman"
For example as part of my aggregation I want to group all "types" and have a map containing the artists name as the key and the songs as a value. For an output like :
[_id : artist, myMap : ["David Bowie" : "Starman"]]
But I'm not sure if this is even possible. I've tried using Mongos $push operation but it doesn't like it when I try to push a map. Has anyone attempted this before?
Heres the query I tried :
db.music.aggregate(
{
$group: {
_id: "$type",
myMap : { $push: {"$artist" : "$song" }}
}
}
)
This may be close to what you are looking for:
db.music.aggregate(
{$project :
{
"type" : 1,
foo : { "artist" : "$artist" , "song" : "$song"}
}
},
{$group :
{ "_id" : "$type", "map" : {$push: "$foo" }}
}
)
I think you can not do that with MongoDB. But there is a Jira Ticket asking for that feature.

Save Subset of MongoDB Collection to Another Collection

I have a set like so
{date: 20120101}
{date: 20120103}
{date: 20120104}
{date: 20120005}
{date: 20120105}
How do I save a subset of those documents with the date '20120105' to another collection?
i.e db.subset.save(db.full_set.find({date: "20120105"}));
I would advise using the aggregation framework:
db.full_set.aggregate([ { $match: { date: "20120105" } }, { $out: "subset" } ])
It works about 100 times faster than forEach at least in my case. This is because the entire aggregation pipeline runs in the mongod process, whereas a solution based on find() and insert() has to send all of the documents from the server to the client and then back. This has a performance penalty, even if the server and client are on the same machine.
Here's the shell version:
db.full_set.find({date:"20120105"}).forEach(function(doc){
db.subset.insert(doc);
});
Note: As of MongoDB 2.6, the aggregation framework makes it possible to do this faster; see melan's answer for details.
Actually, there is an equivalent of SQL's insert into ... select from in MongoDB. First, you convert multiple documents into an array of documents; then you insert the array into the target collection
db.subset.insert(db.full_set.find({date:"20120105"}).toArray())
The most general solution is this:
Make use of the aggregation (answer given by #melan):
db.full_set.aggregate({$match:{your query here...}},{$out:"sample"})
db.sample.copyTo("subset")
This works even when there are documents in "subset" before the operation and you want to preserve those "old" documents and just insert a new subset into it.
Care must be taken, because the copyTo() command replaces the documents with the same _id.
There's no direct equivalent of SQL's insert into ... select from ....
You have to take care of it yourself. Fetch documents of interest and save them to another collection.
You can do it in the shell, but I'd use a small external script in Ruby. Something like this:
require 'mongo'
db = Mongo::Connection.new.db('mydb')
source = db.collection('source_collection')
target = db.collection('target_collection')
source.find(date: "20120105").each do |doc|
target.insert doc
end
Mongodb has aggregate along with $out operator which allow to save subset into new collection. Following are the details :
$out Takes the documents returned by the aggregation pipeline and writes them to a specified collection.
The $out operation creates a new collection in the current database if one does not already exist.
The collection is not visible until the aggregation completes.
If the aggregation fails, MongoDB does not create the collection.
Syntax :
{ $out: "<output-collection>" }
Example
A collection books contains the following documents:
{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }
{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }
{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }
{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }
{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
The following aggregation operation pivots the data in the books collection to have titles grouped by authors and then writes the results to the authors collection.
db.books.aggregate( [
{ $group : { _id : "$author", books: { $push: "$title" } } },
{ $out : "authors" }
] )
After the operation, the authors collection contains the following documents:
{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
{ "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
In the asked question, use following query and you will get new collection named 'col_20120105' in your database
db.products.aggregate([
{ $match : { date : "20120105" } },
{ $out : "col_20120105" }
]);
You can also use $merge aggregation pipeline stage.
db.full_set.aggregate([
{$match: {...}},
{ $merge: {
into: { db: 'your_db', coll: 'your_another_collection' },
on: '_id',
whenMatched: 'keepExisting',
whenNotMatched: 'insert'
}}
])