Query in Embedded document - mongodb

I want to do a query where i can reach all banks where bank is banco1 and investments are not equal to "box".
How can i do that? I tried this query, but don't work :
db.banks.find( { "investments": { $elemMatch: { bank: "banco1", productName: {$ne:"box} } } } );
This query it's one example of many that i have tried.
Thanks.
{
"_id": "5d3fc8c3914297c7b9a3a9e5",
"banco": "banco 1",
"investimentos": [{
"bank": "banco1",
"risk": "Conservador",
"expiryDate": "2021-10-04",
"tax": "1.02",
"discriminator": "investment",
"productName": "LCI"
},
{
"bank": "banco1",
"risk": "Conservador",
"expiryDate": "2020-06-24",
"tax": "0.75",
"discriminator": "investment",
"productName": "Fundo DI"
},
{
"bank": "banco1",
"risk": "Conservador",
"tax": "0.04",
"discriminator": "investment",
"id": "259ad8ac-57b7-4d33-8e75-46cf5c5c28e3",
"aniversary": "30",
"productName": "box"
}
}],
{
"_id": "5d3fcb4c914297c7b9a3a9e6",
"banco": "banco2",
"investimentos": [{
"bank": "banco2",
"risk": "Conservador",
"expiryDate": "2020-06-24",
"tax": "0.80",
"discriminator": "investment",
"id": "73db503f-c780-448c-a6a8-05d2837ff6ff",
"redemptionDate": "D+1",
"productName": "Fundo DI"
}
,
{
"bank": "banco2",
"risk": "Conservador",
"expiryDate": "2020-12-17",
"tax": "0.98",
"discriminator": "investment",
"id": "54e01515-dc7f-470f-8f00-8603c8f00686",
"productName": "LCA"
},
{
"bank": "banco2",
"risk": "Conservador",
"expiryDate": "2021-08-05",
"tax": "1.0",
"discriminator": "investment",
"id": "259ad8ac-57b7-4d33-8e75-46cf5c5c28e2",
"productName": "CDB"
}
}]

Welcome to SO Luan!
I've found that Mongo Aggregations give me all of the power available in find, and so much more functionality. So about 95% of the time, unless I'm doing a quick query, I end up going to aggregates.
The following aggregate will get you what you're looking for:
db.getCollection('Test').aggregate([
{ $unwind: "$investimentos"},
{ $match: {
'investimentos.bank': "banco1",
'investimentos.productName': /^(?!box$)/
}},
])
The $unwind pipeline takes the array of embedded documents in investimentos, and creates a new document, retaining the parent fields (_id and banco), for each element withing investimentos. You can try just db.getCollection('Test').aggregate([{ $unwind: "$investimentos"}]) to visually see what happens.
The $match pipeline does the same thing as the find method - you give it a list of filters to apply. The second element uses a regex (surrounded by '/' chars) to specify that it should find everything that does not start with "box".

Related

How to have multiple nested arrays in an array

I have the collection data from a csv file with header. When i run my query
db.ties.aggregate(
[
{
$group:
{
_id: { "SHOP": "$SHOP" },
isLinkedTo: { $push: { "PERSON": "$PERSON", "CITY": "$CITY", "ROOM": "$ROOM", "STYLE": "$STYLE", "hasDonated": {"DATE": "$DATE", "OBJECT": "$OBJECT", "COST": "$COST", "COLOR": "$COLOR", "PAYMENT": "$PAYMENT"}}}
}
},
{ $out: "ties"}
],
{ allowDiskUse: true }
)
I have like result:
{
"_id": {
"Shop": "FirstShopNameCovered"
},
"isLinkedTo": [{
"PERSON": "Carleen",
"CITY": "Rome",
"ROOM": "Kitchen",
"STYLEPREFERED": "Modern",
"hasDonated": {
"DATE": "2019-10-11",
"OBJECT": "Set of dishes",
"COST": 72,
"COLOR": "White",
"PAYMENT": "Credit card"
}
}, {
"PERSON": "Carleen",
"CITY": "Rome",
"ROOM": "Kitcher",
"STYLEPREFERED": "Modern",
"hasDonated": {
"DATE": "2018-10-26",
"OBJECT": "Set of chairs",
"COST": 353,
"COLOR": "Grey",
"PAYMENT": "Coupon"
}
}, {
"PERSON": "Pernick",
"CITY": "Venezia",
"ROOM": "Bathroom",
"STYLE": "Minimalist",
"hasDonated": {
"DATE": "2018-09-18",
"OBJECT": "Mirror",
"COST": 68,
"COLOR": "Brown",
"PAYMENT": "Credit card"
}
}
You can see that there is replicated the Person "PERSON": "Carleen" with all data with 2 different arrays hasDonated.
I wish have something like this result, with person not replicated that contains all hasDonated arrays where he is present:
"_id": {
"Shop": "NameCovered"
},
"isLinkedTo": [{
"PERSON": "Carleen",
"CITY": "Rome",
"ROOM": "Kitchen",
"STYLE": "RetrĂ²",
"hasDonated": {
"DATE": "2019-10-11",
"OBJECT": "Set of dishes",
"COST": 72,
"COLOR": "White",
"PAYMENT": "Credit card"
},
{
"DATE": "2018-10-26",
"OBJECT": "Chair",
"COST": 53,
"COLOR": "Grey",
"PAYMENT": "Coupon"
}
}, {
"PERSON": "Pernick",
"CITY": "Venezia",
"ROOM": "Bathroom",
"STYLE": "Minimalist",
"hasDonated": {
"DATE": "2018-09-18",
"OBJECT": "Mirror",
"COST": 68,
"COLOR": "Brown",
"PAYMENT": "Credit card"
}
How can I do to have the result like this?
First we need to $unwind to flat the array. Then group the hasDonated using $group where unique is found by combination of "_id" and "PERSON" as you mentioned.
[
{
"$unwind": "$isLinkedTo"
},
{
$group: {
_id: {
_id: "$_id",
per: "$isLinkedTo.PERSON"
},
isLinkedTo: {
$first: {
PERSON: "$isLinkedTo.PERSON",
CITY: "$isLinkedTo.CITY",
ROOM: "$isLinkedTo.ROOM",
STYLEPREFERED: "$isLinkedTo.STYLEPREFERED"
}
},
hasDonated: {
$addToSet: "$isLinkedTo.hasDonated"
}
}
},
{
$addFields: {
_id: "$_id._id",
"isLinkedTo.hasDonated": "$hasDonated"
}
},
{
$project: {
hasDonated: 0
}
},
{
$group: {
_id: "$_id",
isLinkedTo: {
$push: "$isLinkedTo"
}
}
}
]
Working Mongo playground

How to group the documents which I just unwinded in mongodb?

This is the initial document. I applied $unwind on "colors" and another $unwind on "colors.sizes". But now am not able to group them back. How is that done?
{
"_id": ObjectId("5ef838255c959771c46fc917"),
"created": ISODate("2017-03-04T18:30:00.000Z"),
"updated": ISODate("2020-06-28T00:49:23.000Z"),
"status": "active",
"productId": "3828",
"tags": {
"colors": ["BEIGE", "BLACK", "BLUE", "GREEN", "GREY", "LIGHT BEIGE", "LIGHT PINK", "NAVY", "PINK", "WHITE"],
"size": ["39", "39.5", "40", "40.5", "41", "41.5", "42", "42.5", "43", "43.5", "44", "44.5", "45", "45.5", "46"],
"category": ["SHOES", "SNEAKERS", "MENS SHOES & ACCESSORIES"],
"price": ["3400.00", "1360.00", "2040.00", "1700.00", "45575.00"],
"season": ["SS(2020)", "AW(2019)", "SS(2019)", "AW(2018)", "SS(2017)", "AW(2016)", "SS(2016)", "SS(2015)"],
"hash": ["ACTIVE", "ANKLE", "BLUSH", "COLLAR", "COLOUR", "DESIGNER", "FOIL", "GOLD-TONE", "LACE-UP", "LEATHER", "LOW", "LOW TOP", "ORIGINAL", "PADDED", "ROUND", "ROUND TOE", "RUBBER", "SIGNATURE", "TONAL"]
},
"name": "ORIGINAL ARCHILLES LOW SNEAKER",
"description": "",
"brand": "COMMON PROJECTS",
"styleCode": "220036200",
"availableOnline": true,
"colors": [{
"images": [],
"_id": ObjectId("5ef838255c959771c46fc918"),
"colorId": "1",
"color": "WHITE",
"hexCode": "#FFFFFF",
"status": "active",
"sizes": [{
"extraInfo": [{
"title": "Size And Fit",
"text": "Product measures: Heel 3cm (1.2\"). True to size"
}, {
"title": "Information",
"text": "Designer colour: White. Leather. Rubber outsole. Made in Italy"
}],
"_id": ObjectId("5ef838255c959771c46fc919"),
"sizeId": "1",
"neo": "0220003258813",
"size": "39",
"originalPrice": "3400.00",
"sellingPrice": "3400.00",
"discountPercent": "0.00",
"status": "active"
}, {
"extraInfo": [{
"title": "Size And Fit",
"text": "Product measures: Heel 3cm (1.2\"). True to size"
}, {
"title": "Information",
"text": "Designer colour: White. Leather. Rubber outsole. Made in Italy"
}],
"_id": ObjectId("5ef838255c959771c46fc91a"),
"sizeId": "2",
"neo": "0220111220849",
"size": "39.5",
"originalPrice": "3400.00",
"sellingPrice": "3400.00",
"discountPercent": "0.00",
"status": "active"
}, {
"extraInfo": [{
"title": "Size And Fit",
"text": "Product measures: Heel 3cm (1.2\"). True to size"
}, {
"title": "Information",
"text": "Designer colour: White. Leather. Rubber outsole. Made in Italy"
}],
"_id": ObjectId("5ef838255c959771c46fc91b"),
"sizeId": "3",
"neo": "0220004218564",
"size": "40",
"originalPrice": "3400.00",
"sellingPrice": "3400.00",
"discountPercent": "0.00",
"status": "active"
}]
}, ]
}
db.getCollection('products').aggregate([
{$match:{"productId":"3828"}},
{$unwind:"$colors"},
{$unwind:"$colors.sizes"},
{
$lookup: {
from:"inventories",
localField: "colors.sizes.neo",
foreignField: "sku",
as:"colors.sizes.stores"
}
}
])
The opposite operator of $unwind is $group
Explanation
We $group by productId + colors[*]._id fields grouping root, colors and sizes separately.
Note: You can define each variable separately like this:
created: {$first:"$created"},
updated: {$first:"$updated"},
...
OR
use aggregation variable $$ROOT which contains entire document fields
We "merge" each variables into single object and move into root level.
Note: If we don't use $replaceRoot, the data structure will looks like this:
{
_id: "...",
root: {
_id: "...",
...
colors:{
...
sizes:[...]
}
}
}
Note: Unfortunately, MongoDB doesn't allow merge subkeys like this:
{
$mergeObjects:[
"$root",
{"colors":"$colors"},
{"colors.sizes":"$sizes"} <-- Not allowed
]
}
So we have to use $objectToArray + $map + $arrayToObject operators
$objectToArray - transforms object into array
{"hello": "world"} ---> [{"k":"hello", "v":"world"}]
$map - iterates array and transforms array content.
In this case, we replace `sizes` field with `sizes` from the 1st step
$objectToArray - transforms "special" array into object
[{"k":"hello", "v":"world"}] ---> {"hello": "world"}
Now we group only by productId and group colors and root values separately
We finish merging variables and transform into desired object (same as 2nd step)
Add these steps into your pipeline:
{
$group: {
"_id": {
productId: "$productId",
colors: "$colors._id"
},
"root": {
$first: "$$ROOT"
},
"colors": {
$first: "$colors"
},
"sizes": {
$push: "$colors.sizes"
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$root",
{
"colors": {
$arrayToObject: {
$map: {
input: { $objectToArray: "$colors" },
as: "color",
in: {
k: "$$color.k",
v: {
$cond: [
{ $eq: [ "$$color.k", "sizes" ] },
"$sizes",
"$$color.v"
]
}
}
}
}
}
}
]
}
}
},
{
$group: {
"_id": "$_id",
"root": {
$first: "$$ROOT"
},
"colors": {
$push: "$colors"
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$root",
{ "colors": "$colors" }
]
}
}
}
MongoPlayground

MongoDb Query db.<collectionname>.find()

I have this Document for mongodb im working on.
The query i m running is showing no output even though there is
one field with score < 5
{
"address": {
"building": "1007",
"coord": [ -73.856077, 40.848447 ],
"street": "Morris Park Ave",
"zipcode": "10462"
},
"borough": "Bronx",
"cuisine": "Bakery",
"grades": [
{ "date": { "$date": 1393804800000 }, "grade": "A", "score": 2 },
{ "date": { "$date": 1378857600000 }, "grade": "A", "score": 6 },
{ "date": { "$date": 1358985600000 }, "grade": "A", "score": 10 },
{ "date": { "$date": 1322006400000 }, "grade": "A", "score": 9 },
{ "date": { "$date": 1299715200000 }, "grade": "B", "score": 14 }
],
"name": "Morris Park Bake Shop",
"restaurant_id": "30075445"
}
db.rest.find({grades : {"score":{$lt:5}}});
The reason that your query isn't getting the expected result is that you're not quite using the correct syntax for querying a field in an embedded document. The correct syntax is
db.rest.find({ "grades.score" : {$lt:5}})
This syntax uses the dot notation to specify the path to the field in the embedded document that you are querying for.
The syntax you used means something a tad different. It queries for a document that has an embedded document which is an exact match to the document in your query rather than looking for a document that has an embedded document with a field in it that matches your search criteria.
To illustrate the difference, let's assume you also have the following document in the collection.
{
"address": {
"building": "1007",
"coord": [ -73.856077, 40.848447 ],
"street": "Morris Park Ave",
"zipcode": "10462"
},
"borough": "Bronx",
"cuisine": "Bakery",
"grades": [
{ "score": 2 },
{ "score": 6 },
{ "score": 10 },
{ "score": 9 },
{ "score": 14 }
],
"name": "Morris Park Bake Shop",
"restaurant_id": "30075445"
}
If we now run the following query, it finds the second document, and only the second document:
db.rest.find({grades : {"score":2}})
This is because this type of query translates the embedded document you specified as the search criterion into its BSON representation and then looks for an exact match to the supplied document rather than apply the criterion you were looking for to every embedded document that has a 'score' field.
If run the query using the

mongoDB search query

For an assignment, we are given the following code:
db.bib.insertMany( [
{type : "book",
"#year": "1994",
"title": "TCP/IP Illustrated",
"author": {
"last": "Stevens",
"first": "W."
},
"publisher": "Addison-Wesley",
"price": "65"
},
{type : "book",
"#year": "1992",
"title": "Unix Programming",
"author": {
"last": "Stevens",
"first": "W."
},
"publisher": "Addison-Wesley",
"price": "65"
},
{type : "book",
"#year": "2000",
"title": "Data on the Web",
"author": [
{
"last": "Abiteboul",
"first": "Serge"
},
{
"last": "Buneman",
"first": "Peter"
},
{
"last": "Suciu",
"first": "Dan"
}
],
"publisher": "Morgan Kaufmann",
"price": "39"
},
{type : "book",
"#year": "1999",
"title": "Digital TV",
"editor": {
"last": "Gerbarg",
"first": "Darcy",
"affiliation": "CITI"
},
"publisher": "Kluwer",
"price": "130"
}
,
{type : "journal",
"title": "Irreproducible results",
"editor": {
"last": "Self",
"first": "W."
},
"publisher": "SV"
}
])
Using mongoDB, we are then asked to complete different search queries in order to find the desired information. The one that I am currently stuck on is
List the titles of books published after 1995 and costing less than 100.
From my understanding, the proper query should be something along the lines of
db.bib.find({price: {$lt: 100}, year: {$gt: 1995}}, {title: 1, _id: 0})
However, this provides a blank result when it should not. Why is this and how can I fix it?
Data type for both of the field is string. you can not compare them as numeric. Try following and it will work. using collation, you can ask MongoDB to treat them as int. you can read more about collation here at mongodb.com.
db.bib.find({$and: [{"#year": {$gt: "1995"}}, {price: {$lt: "100"}}]}).collation({
locale: "en_US",
numericOrdering: true
});

select documents grouped by field

I have this documents of movie showing time and date:
`{
"_id": ObjectId("5628668c3e82c49245b7acdc"),
"ticketID": ObjectId("5606d36b5fbd7d76028b4b08"),
"uid": "50000",
"day": "Friday",
"date": "2015-10-23 21:05:00",
"adult": NumberLong(550),
"student": NumberLong(550),
"children": NumberLong(250),
"limit": NumberLong(20),
"sold": NumberLong(0)
},{
"_id": ObjectId("562866013e82c49045b7acdc"),
"ticketID": ObjectId("5606d36b5fbd7d76028b4b08"),
"uid": "50000",
"day": "Friday",
"date": "2015-10-23 19:30:00",
"adult": NumberLong(1050),
"student": NumberLong(800),
"children": NumberLong(550),
"limit": NumberLong(20),
"sold": NumberLong(0)
},{
"_id": ObjectId("562865013e82c49845b7acda"),
"ticketID": ObjectId("5606d36b5fbd7d76028b4b08"),
"uid": "50000",
"day": "Friday",
"date": "2015-10-23 18:45:00",
"adult": NumberLong(1500),
"student": NumberLong(750),
"children": NumberLong(750),
"limit": NumberLong(20),
"sold": NumberLong(0)
}
`
I want to group the final result by "day" where "ticketID" matches, "date" as an object and adding "adult","student","children","limit" to an array
-- UPDATE --
I would like the returned structure to follow:
{
"_id": "ticketID",
"day": "Friday",
"items": [
{
"date": date,
"time": time"adult": price,
"children": price,
"student": price,
"limit": value
},
{
"date": date,
"time": time"adult": price,
"children": price,
"student": price,
"limit": value
},
{
"date": date,
"time": time"adult": price,
"children": price,
"student": price,
"limit": value
}
]
}
Run the following aggregation pipeline which uses the $group operator to group your documents by the specified fields, add the items array by using the accumulator operator $push that returns an array of expression values for each group. The $project pipeline operator then reshapes the documents by amending the fields to get the final desired structure:
var pipeline = [
{
"$group": {
"_id": {
"ticketID": "$ticketID",
"day": "$day"
},
"items": {
"$push": {
"date": "$date",
"time": "$time",
"adult": "$adult",
"children": "$children",
"student": "$student",
"limit": "$limit"
}
}
}
},
{
"$project": {
"_id": "$_id.ticketID",
"day": "$_id.day",
"items": 1
}
}
];
db.collection.aggregate(pipeline);