PyMongo: Removing a nested object without knowing the key - mongodb

Let's say I have a collection that looks like this:
{
'_id': ObjectId('abc'),
'customer': 'bob',
'products': {
'1234':
{'name': 'Shirt',
'productID': 5
},
'5679': {
'name': 'Hat',
'productID': 5
}
}
'1011': {
'name': 'Jeans',
'productID': 9
}
}
}
I am looking to remove all nested objects whose 'productID' property is 5, so the collection would look like this afterwards:
{'_id': ObjectId('abc'),
'name': 'bob',
'products': {
'1011': {
'name': 'Jeans',
'productID': 9
}
}
}
I know the following information:
customer: bob
productID: 5
Is it possible to do a wildcard on 'products'? Something like this (it does not work):
db.update({'customer':'bob'}, {'$unset': {'products.*': {'productID': 9}})

If you have a choice, refactor your data to make each item a list element, e.g.
{
'customer': 'bob',
'products': [
{'code': '1234',
'name': 'Shirt',
'productID': 5
},
{'code': '5679',
'name': 'Hat',
'productID': 5
},
{'code': '1011',
'name': 'Jeans',
'productID': 9
}
]
}
Then your update becomes a piece of cake:
db.mycollection.update_one({'customer': 'bob'}, {'$pull': {'products': {'productID': 5}}})
result:
{
"customer": "bob",
"products": [
{
"code": "1011",
"name": "Jeans",
"productID": 9
}
]
}
Persisting with poor choices of schema will no yield long term rewards.

Related

Flutter: How to sort a List on index data from another List

I have a List named "products" and in there I have data consisting of product data such as name, price, and category. It follows the structure like this,
{
"name": "Milk Shake Strawberry",
"price": "250",
"category": "Drinks",
"categoryID:1234
},
{
"name": "Swiss Roll",
"price": "150",
"category": "Cake",
"categoryID":1235
}
I can show this in a ListView using ListView.builder method.
But I want to sort this List with an index number which I can get from a different API request. In there get the Category data like this,
{
"index": "0",
"categoryName": "Drinks",
"categoryID:1234
},
{
"index": "1",
"categoryName": "Cake",
"categoryID":1235
}
It is not possible to store the index inside the product List since there is an interface to change the index (order) on the categories.
How can I orderBy these product data using the second API request response data(index)?
(All these data are stored in firestore collections)
I don't know if below is not what you are looking for:
final products = [
{
'name': 'Milk Shake Strawberry',
'price': '250',
'category': 'Drinks',
'categoryID': 1234
},
{
'name': 'Swiss Roll',
'price': '150',
'category': 'Cake',
'categoryID': 1235
}
];
final apiOrderData = [
{'index': '0', 'categoryName': 'Drinks', 'categoryID': 1234},
{'index': '1', 'categoryName': 'Cake', 'categoryID': 1235}
];
final orderedProducts = apiOrderData
.map(
(order) => products.firstWhere(
(product) => order['categoryID'] == product['categoryID'],
),
)
.toList();

Flutter: how to return the value by using other value inside the List<Map>

I have a List<Map<String, String>> like below
[
{ 'name': 'John', 'id': 'aa' },
{ 'name': 'Jane', 'id': 'bb' },
{ 'name': 'Lisa', 'id': 'cc' },
]
And, the ID list **List** as ['bb', 'aa']. By using the ID list, I want to return a new list ['Jane', 'John'] as **List _selectedList**.
I have tried to do it with the .**indexWhere**, however, I am stuck on the List where it has more than one value.
How can I return the List only with the name-value when there is more than one value to look for?
void main() {
var a = [
{ 'name': 'John', 'id': 'aa' },
{ 'name': 'Jane', 'id': 'bb' },
{ 'name': 'Lisa', 'id': 'cc' },
];
var b = ['bb', 'aa'];
var c = a.where((m) => b.contains(m['id'])).map((m) => m['name']);
print(c);
}
Result
(John, Jane)
Use a set to filter out the IDs efficiently.
var ids = ["aa", "cc"];
var idSet = Set<String>.from(ids);
var json = [
{ 'name': 'John', 'id': 'aa' },
{ 'name': 'Jane', 'id': 'bb' },
{ 'name': 'Lisa', 'id': 'cc' },
];
var _selectedList = json.where((data) => idSet.contains(data["id"]))
.map((data) => data["name"]).toList();
print(_selectedList);
Here, .where filters out the data where the ID matches one in the input list "IDs". Sets make the process efficient. Then, the resultant objects are passed to .map where the "name" field is extracted. Finally, the whole thing is converted into a list thus returning the list of names.
Output of the above code:
[John, Lisa]

Aggregate across two collections and update documents in first collect in pymongo

I am using pymongo. I have a collection for which I want to update fields based on values from another collection.
Here's a document from the collection1.
{ _id: ObjectId("5fef7a23d0bdc785d4fc94e7"),
path: 'path1.png',
type: 'negative',
xmin: NaN,
ymin: NaN,
xmax: NaN,
ymax: NaN}
And from collection2:
{ _id: ObjectId("5fef7a24d0bdc785d4fc94e8"),
path: 'path1.png',
xmin: 200,
ymin: 200,
xmax: 300,
ymax: 300}
How do I update collection 1 so that the example document looks like:
{ _id: ObjectId("5fef7a23d0bdc785d4fc94e7"),
path: 'path1.png',
type: 'negative,
xmin: 200,
ymin: 200,
xmax: 300,
ymax: 300}
Fetch collection2 into a dict variable and use $set to update collection1, e.g.
for doc in db.collection2.find({}, {'_id': 0}):
db.collection1.update_one({'path': doc.get('path')}, {'$set': doc})
I have found a way to output it into a separate collection, but still not sure how to get it to the same collection.
db.collection1.aggregate[
{
'$match': {
'xmin': 'NaN'
}
}, {
'$lookup': {
'from': 'collection2',
'localField': 'path',
'foreignField': 'path',
'as': 'inferences'
}
}, {
'$project': {
'inferences.xmin': 1,
'inferences.ymin': 1,
'inferences.xmax': 1,
'inferences.ymax': 1,
'path': 1,
'type': 1,
'_id': 0
}
}, {
'$unwind': {
'path': '$inferences',
'preserveNullAndEmptyArrays': False
}
}, {
'$addFields': {
'xmin': '$inferences.xmin',
'ymin': '$inferences.ymin',
'xmax': '$inferences.xmax',
'ymax': '$inferences.ymax'
}
}, {
'$project': {
'path': 1,
'type': 1,
'xmin': 1,
'ymin': 1,
'xmax': 1,
'ymax': 1
}
}, {
'$out': 'collection3'
}
]

Adding a field in Document in MongoDb

database = {
'__v': 78,
'_id': ObjectId('5de4218d6a2be815b9e215e1'),
'services': [
{
'_id': ObjectId('5de4218e6a2be815b9e2186d'),
'name':'shivam',
},
{
'_id': ObjectId('5de4218e6a2be815b9e2181e'),
'name': 'Shivi'.
}
]
}
catalogues.update_one({'services._id': ObjectId(id)},{'$set': {'age':
"30"}},False,True)
This is one of the documents in mongodb.collection. How can I add a field 'age' in the dictionaries placed in services(list/array) in pymongo and update it in the database.
You need to use the $ positional operator
catalogues.update_one({'services._id': ObjectId(x_id)}, {'$set': {'services.$.age': '30'}})
Full example:
from pymongo import MongoClient
from bson import ObjectId
import pprint
db = MongoClient()['mydatabase']
catalogues = db.catalogues
catalogues.insert_one({
'__v': 78,
'_id': ObjectId('5de4218d6a2be815b9e215e1'),
'services': [
{
'_id': ObjectId('5de4218e6a2be815b9e2186d'),
'name': 'shivam'
},
{
'_id': ObjectId('5de4218e6a2be815b9e2181e'),
'name': 'Shivi'
}
]
})
x_id = '5de4218e6a2be815b9e2181e'
catalogues.update_one({'services._id': ObjectId(x_id)}, {'$set': {'services.$.age': '30'}})
result:
{'__v': 78,
'_id': ObjectId('5de4218d6a2be815b9e215e1'),
'services': [{'_id': ObjectId('5de4218e6a2be815b9e2186d'), 'name': 'shivam'},
{'_id': ObjectId('5de4218e6a2be815b9e2181e'),
'age': '30',
'name': 'Shivi'}]}

MongoDB: multiple $elemMatch

I have MongoDB documents structured like this:
{_id: ObjectId("53d760721423030c7e14266f"),
fruit: 'apple',
vitamins: [
{
_id: 1,
name: 'B7',
usefulness: 'useful',
state: 'free',
}
{
_id: 2,
name: 'A1',
usefulness: 'useful',
state: 'free',
}
{
_id: 3,
name: 'A1',
usefulness: 'useful',
state: 'non_free',
}
]
}
{_id: ObjectId("53d760721423030c7e142670"),
fruit: 'grape',
vitamins: [
{
_id: 4,
name: 'B6',
usefulness: 'useful',
state: 'free',
}
{
_id: 5,
name: 'A1',
usefulness: 'useful',
state: 'non_free',
}
{
_id: 6,
name: 'Q5',
usefulness: 'non_useful',
state: 'non_free',
}
]
}
I want to query and get all the fruits which have both {name: 'A1', state: 'non_free'} and {name: 'B7', state: 'free'}.
In the worst case I want at least to count these entries if getting them is not possible and if the equivalent code exists for pymongo, to know how to write it.
For the given example I want to retrieve only the apple (first) document.
If I use $elemMatch it works only for one condition, but not for both. E.g. if I query find({'vitamins': {'$elemMatch': {'name': 'A1', 'state': 'non_free'}, '$elemMatch': {'name': 'B7', 'state': 'free'}}}) it will retrieve all the fruits with {'name': 'B7', 'state': 'free'}.
In this case you can use the $and-operator .
Try this query:
find({
$and: [
{'vitamins': {'$elemMatch': {'name': 'A1', 'state': 'non_free'} } },
{'vitamins': {'$elemMatch': {'name': 'B7', 'state': 'free'} } }
]
});
To explain why you received only the result matching the second criteria: The objects inside each {} you pass to MongoDB are key/value pairs. Each key can only exist once per object. When you try to assign a value to the same key twice, the second assignment will override the first. In your case you assigned two different values to the key $elemMatch in the same object, so the first one was ignored. The query which actually arrived in MongoDB was just find({'vitamins': {'$elemMatch': {'name': 'B7', 'state': 'free'}}}).
Whenever you need to apply the same operator to the same key twice, you need to use $or or $and.
var fruits = db.fruits.find({
"vitamins": {
$all: [{
$elemMatch: {
"name": "A1",
"state": "non_free"
}
}, {
$elemMatch: {
"name": "B7",
"state": "free"
}
}]
}
})
let query = [];
query.push({
id: product.id,
});
query.push({ date });
for (const slot of slots) {
query.push({
slots: {
$elemMatch: {
id: slot.id,
spots: { $gte: slot.spots },
},
},
});
}
const cal = await Product.findOne({ $and: query });