MongoDB: How to get the object names in collection? - mongodb

and think you in advance for the help. I have recently started using mongoDB for some personal project and I'm interested in finding a better way to query my data.
My question is: I have the following collection:
{
"_id" : ObjectId("5dbd77f7a204d21119cfc758"),
"Toyota" : {
"Founder" : "Kiichiro Toyoda",
"Founded" : "28 August 1937",
"Subsidiaries" : [
"Lexus",
"Daihatsu",
"Subaru",
"Hino"
]
}
}
{
"_id" : ObjectId("5dbd78d3a204d21119cfc759"),
"Volkswagen" : {
"Founder" : "German Labour Front",
"Founded" : "28 May 1937",
"Subsidiaries" : [
"Audi",
"Volkswagen",
"Skoda",
"SEAT"
]
}
}
I want to get the object name for example here I want to return
[Toyota, Volkswagen]
I have use this method
var names = {}
db.cars.find().forEach(function(doc){Object.keys(doc).forEach(function(key){names[key]=1})});
names;
which gave me the following result:
{ "_id" : 1, "Toyota" : 1, "Volkswagen" : 1 }
however, is there a better way to get the same result and also to just return the names of the objects. Thank you.

I would suggest you to change the schema design to be something like:
{
_id: ...,
company: {
name: 'Volkswagen',
founder: ...,
subsidiaries: ...,
...<other fields>...
}
You can then use the aggregation framework to achieve a similar result:
> db.test.find()
{ "_id" : 0, "company" : { "name" : "Volkswagen", "founder" : "German Labour Front" } }
{ "_id" : 1, "company" : { "name" : "Toyota", "founder" : "Kiichiro Toyoda" } }
> db.test.aggregate([ {$group: {_id: null, companies: {$push: '$company.name'}}} ])
{ "_id" : null, "companies" : [ "Volkswagen", "Toyota" ] }
For more details, see:
Aggregation framework
$group
Accumulator operators
As a bonus, you can create an index on the company.name field, whereas you cannot create an index on varying field names like in your example.

Related

Spring data MongoDb query based on last element of nested array field

I have the following data (Cars):
[
{
"make" : “Ferrari”,
"model" : “F40",
"services" : [
{
"type" : "FULL",
“date_time" : ISODate("2019-10-31T09:00:00.000Z"),
},
{
"type" : "FULL",
"scheduled_date_time" : ISODate("2019-11-04T09:00:00.000Z"),
}
],
},
{
"make" : "BMW",
"model" : “M3",
"services" : [
{
"type" : "FULL",
"scheduled_date_time" : ISODate("2019-10-31T09:00:00.000Z"),
},
{
"type" : "FULL",
“scheduled_date_time" : ISODate("2019-11-04T09:00:00.000Z"),
}
],
}
]
Using Spring data MongoDb I would like a query to retrieve all the Cars where the scheduled_date_time of the last item in the services array is in-between a certain date range.
A query which I used previously when using the first item in the services array is like:
mongoTemplate.find(Query.query(
where("services.0.scheduled_date_time").gte(fromDate)
.andOperator(
where("services.0.scheduled_date_time").lt(toDate))),
Car.class);
Note the 0 index since it's first one as opposed to the last one (for my current requirement).
I thought using an aggregate along with a projection and .arrayElementAt(-1) would do the trick but I haven't quite got it to work. My current effort is:
Aggregation agg = newAggregation(
project().and("services").arrayElementAt(-1).as("currentService"),
match(where("currentService.scheduled_date_time").gte(fromDate)
.andOperator(where("currentService.scheduled_date_time").lt(toDate)))
);
AggregationResults<Car> results = mongoTemplate.aggregate(agg, Car.class, Car.class);
return results.getMappedResults();
Any help suggestions appreciated.
Thanks,
This mongo aggregation retrieves all the Cars where the scheduled_date_time of the last item in the services array is in-between a specific date range.
[{
$addFields: {
last: {
$arrayElemAt: [
'$services',
-1
]
}
}
}, {
$match: {
'last.scheduled_date_time': {
$gte: ISODate('2019-10-26T04:06:27.307Z'),
$lt: ISODate('2019-12-15T04:06:27.319Z')
}
}
}]
I was trying to write it in spring-data-mongodb without luck.
They do not support $addFields yet, see here.
Since version 2.2.0 RELEASE spring-data-mongodb includes the Aggregation Repository Methods
The above query should be
interface CarRepository extends MongoRepository<Car, String> {
#Aggregation(pipeline = {
"{ $addFields : { last:{ $arrayElemAt: [$services,-1] }} }",
"{ $match: { 'last.scheduled_date_time' : { $gte : '$?0', $lt: '$?1' } } }"
})
List<Car> getCarsWithLastServiceDateBetween(LocalDateTime start, LocalDateTime end);
}
This method logs this query
[{ "$addFields" : { "last" : { "$arrayElemAt" : ["$services", -1]}}}, { "$match" : { "last.scheduled_date_time" : { "$gte" : "$2019-11-03T03:00:00Z", "$lt" : "$2019-11-05T03:00:00Z"}}}]
The date parameters are not parsing correctly. I didn't spend much time making it work.
If you want the Car Ids this could work.
public List<String> getCarsIdWithServicesDateBetween(LocalDateTime start, LocalDateTime end) {
return template.aggregate(newAggregation(
unwind("services"),
group("id").last("services.date").as("date"),
match(where("date").gte(start).lt(end))
), Car.class, Car.class)
.getMappedResults().stream()
.map(Car::getId)
.collect(Collectors.toList());
}
Query Log
[{ "$unwind" : "$services"}, { "$group" : { "_id" : "$_id", "date" : { "$last" : "$services.scheduled_date_time"}}}, { "$match" : { "date" : { "$gte" : { "$date" : 1572750000000}, "$lt" : { "$date" : 1572922800000}}}}]

querying Date in mongodb with borders included

I'm evaluating the following query on my collection with fake data:
db.test_result.find({"date": {$gte: ISODate("2021-07-27"), $lte: ISODate("2021-08-31")}}).count()
And despite the fact that I use $lte it does not include the second date value. Is it a bug? If so then how do I make the query so that the left and right borders are included?
Here is what a fake json obj looks like:
{
"nfl": "Some",
"rStatus": false,
"mac": "02:00:00:00:00:00",
"date": "2021-07-27T12:17:57",
"MDCode": "123132132123",
}
With this given input:
{
"_id" : ObjectId("596c6fea53cc7100104628fa"),
"timestamp" : ISODate("2017-07-17T08:06:02.041Z"),
}
{
"_id" : ObjectId("596c7162973f33000fc8bb81"),
"timestamp" : ISODate("2017-07-17T08:12:18.170Z")
}
{
"_id" : ObjectId("596c736c15371f00106b9e3a"),
"timestamp" : ISODate("2017-07-17T08:21:00.291Z")
}
This query:
...find({"timestamp": {$gte: ISODate("2017-07-17T08:06:02.041Z"), $lte: ISODate("2017-07-17T08:12:18.170Z")}})
Would return:
{
"_id" : ObjectId("596c6fea53cc7100104628fa")
"timestamp" : ISODate("2017-07-17T08:06:02.041Z")
}
{
"_id" : ObjectId("596c7162973f33000fc8bb81"),
"timestamp" : ISODate("2017-07-17T08:12:18.170Z")
}
Which is what you would expect, the results match to the dot the 2 borders. So in a nutshell it would be included if it is an exact match, otherwise you would get only the once in between.

Mongodb- using find() method on an Array of Objects only return first match instead of all

Unlike the other question someone asked where they wanted only one item returned. I HAVE one item returned and I need ALL of the matching objects in the array return. However the second object that matches my query is being completely ignored.
This is what one of the items in the item collection looks like:
{
name: "soda",
cost: .50,
inventory: [
{ flavor: "Grape",
amount: 8 },
{ flavor: "Orange",
amount: 4 },
{ flavor: "Root Beer",
amount: 15 }
]
}
Here is the query I typed in to mongo shell:
Items.find({"inventory.amount" : { $lte : 10} } , { name : 1, "inventory.$.flavor" : 1})
And here is the result:
"_id" : ObjectId("59dbe33094b70e0b5851724c"),
"name": "soda"
"inventory" : [
{ "flavor" : "Grape",
"amount" : 8,
}
]
And here is what I want it to return to me:
"_id" : ObjectId("59dbe33094b70e0b5851724c"),
"name": "soda"
"inventory" : [
{ "flavor" : "Grape",
"amount" : 8
},
{ "flavor" : "Orange",
"amount" : 4
}
]
I'm new to mongo and am dabbling to get familiar with it. I've read through the docs but couldn't find a solution to this though it's quite possible I overlooked it. I'd really love some help. Thanks in advance.
first u can get your result by this query
db.Items.find({"inventory.amount" : { $lte : 10} } , { name : 1, "inventory.flavor" : 1 , "inventory.amount" : 1})

How to set keys in mongoDB aggregation?

The idea is to go from a collection of documents like this:
{
"_id" : ObjectId("58ff4fa372ac97344d5672c2"),
"direction" : 1,
"post" : ObjectId("58ff4ea572ac97344d5672c1"),
"user" : ObjectId("586b84239ae9590ab66bd3ad")
}
{
"_id" : ObjectId("58ff4c9f2952d7341d4afc0c"),
"direction" : -1,
"post" : ObjectId("58fc15a3fb3bed0fd54bfd95"),
"user" : ObjectId("586b84239ae9590ab66bd3ad")
}
To this:
[
//post: direction
"58ff4ea572ac97344d5672c1": 1,
"58fc15a3fb3bed0fd54bfd95": -1
]
I can't seem to find anything in the MongoDB Aggregation docs that allows you to set the key name using the value of another field.
I'm expecting this code to work, but I can see why it doesn't. It thinks that "$post" refers to a MongoDB expression.
db.votes.aggregate([
{$group: {
_id: null,
entries: {
$addToSet: {
"$post": "$direction"
}
}
}}
])

mongodb: $orderBy on a field in the first element of a list contained in each document

I have a collection in which element has a list of objects. I would like to use $orderBy on a specific field on the first element of a list of objects that each document has.
For example:
each document represents a user, and each user has a list of sessions. I would like to sort the users on the date stored in the first session of the list.
Maybe something like { $orderby: { "sessions[0].timestamp" : 1 } } ?
Is this possible?
The operation you ask for is a simple one with .sort(). Perhaps you are not aware of the usage of "dot notation" with MongoDB
With the following documents as a minimal example:
{
"name" : "Fred",
"sessions" : [ { "timestamp" : ISODate("2014-06-05T10:38:24.371Z") } ]
}
{
"name" : "Barney",
"sessions" : [ { "timestamp" : ISODate("2014-06-05T10:38:34.557Z") } ]
}
Issue the following query:
db.users.find({},{ _id: 0}).sort({ "sessions.0.timestamp": -1 })
And get the ordered result by the first item of the array, timestamp field:
{
"name" : "Barney",
"sessions" : [ { "timestamp" : ISODate("2014-06-05T10:38:34.557Z") } ]
}
{
"name" : "Fred",
"sessions" : [ { "timestamp" : ISODate("2014-06-05T10:38:24.371Z") } ]
}