Ignoring a field mongodb request - arrays

Let's say we have objects like this in a mongodb collection:
{
_id: 00000001
colors: ["green", "yellow"],
houses: [
{
number: 1,
owner: "John"
},
{
number: 2,
owner: "John"
},
{
number:3,
owner: "Dave"
}
]
},
{
_id: 00000002
colors: ["green", "red"],
houses: [
{
number: 15,
owner: "Dave"
},
]
}
So, to get every object where the color array contains the color green the query I would need to write would look smth like this: collection.find({colors: "green"});
Now if I would like to get all the objects in which John owns a house, how would I formulate such a query?
Basically what I am asking is, if my query would be collection.find({houses: {owner: "John", number: ?}}) what would I need to replace the "?" with to tell mongo that I don't care what value number has.
Or maybe there is another approach that I haven't thought of?
Thank you for any help!
(Btw this is a made up example hence why the IDs look weird and the object in itself doesn't seem very useful.)

To query an array of objects you can use the dot notation, try:
db.collection.find({ "houses.owner": "John"}})

Related

Which is the efficient way of updating an element in MongoDB?

I have a collection like below
{
"doc_id": "1234",
"items": [
{
"item_no": 1,
"item": "car",
},
{
"item_no": 2,
"item": "bus",
},
{
"item_no": 3,
"item": "truck",
}
]
},
I need to update an element inside items list based on a search criteria. My search criteria is, if "item_no" is 3, "item" should be updated to "aeroplane".
I have written the following two approaches in Python to solve this.
Approach 1:
cursor = list(collection.find({"doc_id": 1234}))
for doc in cursor:
if "items" in doc:
temp = deepcopy(doc["items"])
for element in doc["items"]:
if ("item_no" and "item") in element:
if element["item_no"] == 3:
temp[temp.index(element)]["item"] = "aeroplane"
collection.update_one({"doc_id": 1234},
{"$set": {"items": temp}})
Approach 2:
cursor = list(collection.find({"doc_id": 1234}))
for doc in cursor:
if "items" in doc:
collection.find_one_and_update({"doc_id": 1234}, {'$set': {'items.$[elem]': {"item_no": 3, "item": "aeroplane"}}}, array_filters=[{'elem.item_no': {"$eq": 3}}])
Among the above two approaches, which one is better in terms of time complexity?
Use only a query and avoid loops:
db.collection.update({
"doc_id": "1234",
"items.item_no": 3
},
{
"$set": {
"items.$.item": "aeroplane"
}
})
Example here
Note how using "items.item_no": 3 into the find stage you can use $ into update stage to refer the object into the array.
So, doing
{
"doc_id": "1234",
"items.item_no": 3
}
When you use $ you are telling mongo: "Ok, do your action in the object where the condition is match" (i.e., the object in the collection with doc_id: "1234" and an array with items.item_no: 3)
Also if you want to update more than one document you can use multi:true like this example.
Edit: It seems you are using pymongo so you can use multi=True (insted of multi: true) or a cleaner way, using update_many.
collection.update_many( /* your query here */ )

MongoDB Compass GROUP BY value

Lets say that i have the following objects in my mongodb-compass-database:
{
_id: ObjectID("randomString"),
Name: "Test1",
OtherAttribute: 187
},
{
_id: ObjectID("otherRandomString"),
Name: "Test2",
OtherAttribute: 1337
},
{
_id: ObjectID("otherRandomString2"),
Name: "Test1",
OtherAttribute: 23
}
How can I return the "Name"-value if it exist more than one time?
In the example I want to receive "Test1" or the whole object, doesnt matter.
I just need to see if there are any duplicates.
I need to use the MongoDB Compass Find-Input:
Is this possible?

MongoDB update nested array elements

I have the following structure:
{
id: "1",
invoices: [{ id: "1", balance: 1},{ id: "2", balance: 1}]
},
{
id: "2",
invoices: [{ id: "3", balance: 1},{ id: "4", balance: 1}]
}
I'm getting a list of invoices IDs that i shouldn't update, the rest i need to update the balance to 0.
I'm pretty new to MongoDB and managing to find a way to do it.
Let say you want to update all invoices of id 1 except invoice.id 2 try this one:
db.collection.update(
{ id: "1", "invoices.id": {$ne: 2} },
{
$set: {
"invoices.$[]": { balance: 0 }
}
}
)
First of all, you forgot the quotes around the field names. Your documents should be like this:
{
"id": "1",
"invoices": [{
"id": "1",
"balance": 1
}, {
"id": "2",
"balance": 1
}]
}
I have limited experience with MongoDB, as I learnt it this semester at University. However, here is my solution:
db.collection.update(
{ id: "1" },
{
$set: {
"invoices.0": { id: "1", balance: 0 }
}
}
)
What does this solution do?
It takes the document with id 1. That is your first document.
The $set operator replaces the value of a field with the specified value. (straight out from the MongoDB manual - MongoDB Manual - $set operator).
"invoices.0" takes the first invoice from the invoices array and then it updates the balance to 100.
Replace the word collection from db.collection with your collection name.
Try and see if it works. If not, I'd like someone with more experience to correct me.
LE: Now it works, try and see.

how to merge similar values in normalizr function?

I have unusual response from server like this
[
{
id: 1,
name: "Alexandr",
children: [
{
id: 2,
name: "Stephan"
},
{
id: 3,
name: "Nick"
}
]
},
{
id: 4,
name: "David",
children: [
{
id: 3,
name: "Nick"
},
{
id: 6,
name: "Paul"
}
]
}
]
i would like to normalize this response to receive a diction with all people. So, i use normalizr go flat this
const people= new Schema('people');
people.define({
Children: arrayOf(people),
NotOwnChildren: arrayOf(people)
});
let normalized = normalize(response.data, arrayOf(people));
but doing like this i get an error
"When merging two people, found unequal data in their "Children" values. Using the earlier value."
How can i adjust normalizr to merge people with same id (update entities with newest data)?
It looks like you're getting two people that have differing values for one of their keys (I'm assuming your example input is truncated).
For Normalizr#2:
You can use a custom mergeIntoEntity function to resolve the issue manually.
For Normalizr#>=3.0.0, you'll need use mergeStrategy.

Filter array elements by nested array of objects

First, let me say that this question is slightly different to others I've seen regarding nested arrays in mongo.
Most of them ask how to get a specific element in a nested array. I want to get all elements of an array that has itself another array containing a given value.
I have documents, that look like this:
{
_id: something,
value: something,
items: [{
name: someItem,
price: somePrice,
stores:[
{name: storeName, url: someUrl },
{name: anotherStoreName, url: someOtherUrl}
]
},
{
name: someOtherItem,
price: someOtherPrice,
stores:[
{name: storeName, url: someUrl },
{name: yetAnotherStoreName, url: someOtherUrl}
]
}]
}
What I want is to get only the items elements that have a given store in the stores array.
This is, if I ask storeName, I should get both items in the example. But if I ask for anotherStoreName, I should only get the first element of the array items.
Searching for similar questions and trying the solutions I can only get the first matching element of items, but I want all the matching elements.
I'd appreciate any help.
You should use mongo aggregation to get result in following way.
First use $unwind to separate array of items and then add match condition.
db.collection.aggregate([{
$unwind: "$items"
}, {
$match: {
"items.stores.name": "storeName"
}
}, {
$project: {
_id: 0,
items: "$items" //you can add multiple fields here
}
}]);
You should use the aggregation framework to unwind elements on the items array and then treat them as individual documents.
db.data.aggregate([
{
$unwind: "$items"
},
{
$project: {
_id: 0,
items: "$items"
}
},
{
$match: {
"items.stores.name": "yetAnotherStoreName"
}
}
]);
$project just let you work with the important part of the document.
This works on the mongoshell be carefully when using your driver.
The result.

Resources