Mongo find in hash - mongodb

Suppose I have several documents like so
{
title: 'blah',
value: {
"A": {property: "foo"},
"B": {property: "bar"},
"C": {property: "foo"},
"D": {property: "foo"}
}
}
{
title: 'blah2',
value: {
"A": {property: "bar"},
"B": {property: "bar"},
"C": {property: "bar"},
"D": {property: "foo"}
}
}
What mongodb query will get me all of the documents / hash keys that have {property: "foo"}
(I know this can be done using js after the query, but can it be done within the query itself?)

The trouble is that there's no wildcard for object keys (see https://jira.mongodb.org/browse/SERVER-267), so you wouldn't be able to do this without listing all of the keys in your "value". That might be an option if you know what all of the keys are, but I imagine you don't.
If you converted "value" to an array rather than an object, you could do a query easily (which would return the documents, not the hash keys).

As the first answer says, there is nothing in the mongodb query language that would allow you to do this type of query.
You might want to consider altering your schema to make value an array like this:
value: [
{ name : "A", property : "bar" },
{ name : "B", property : "bar" },
{ name : "C", property : "bar" },
{ name : "D", property : "foo" }
]
Then you could index on value.property and run a query on value.property = "foo".

Related

Update a nested field with an unknown index and without affecting other entries

I have a collection with a layout that looks something like this:
student1 = {
"First_Name": "John",
"Last_Name": "Doe",
"Courses": [
{
"Course_Id": 123,
"Course_Name": "Computer Science",
"Has_Chosen_Modules": false
},
{
"Course_Id": 284,
"Course_Name": "Mathematics",
"Has_Chosen_Modules": false
}
]
};
I also have the following update query:
db.Collection_Student.update(
{
$and: [
{First_Name: "John"},
{Last_Name: "Doe"}
]
},
{
$set : { "Courses.0.Has_Chosen_Modules" : true }
}
);
This code will currently update the Computer Science Has_Chosen_Modules value to true since the index is hardcoded. However, what if I wanted to update the value of Has_Chosen_Modules via the Course_Id instead (as the course might not necessarily be at the same index every time)? How would I achieve this without it affecting the other courses that a given student is taking?
You can select any item in the sub array of your document by targeting any property in the sub array of your document by using dot .
You can easily achieve this by the following query.
db.Collection_Student.update(
{
First_Name: "John",
Last_Name: "Doe",
'Courses.Course_Id': 123
},
{
$set : { "Courses.$.Has_Chosen_Modules" : true }
}
);
Conditions in search filter are by default treated as $and operator, so you don't need to specifically write $and for this simple query.

Fetch a certain amount of array using Mongoose and Mongo [duplicate]

Let's say I've got a collection People in mongo database:
[{
id: 1,
name: "Tom",
animals: ["cat", "dog", "fish", "bear"]
},
{
id: 2,
name: "Rob",
animals: ["shark", "snake", "fish", "bear", "panda"]
},
{
id: 3,
name: "Matt",
animals: ["cat", "fish", "bear"]
}]
For the purpose of REST API I need to create a pagination system for viewing people's animals and return only 3 per request. So for example if you go to /people/2the API should return this array:
["shark", "snake", "fish"]
I'm trying to get this result using Mongo methods. Here's my attempt:
db.getCollection('people').find({id: 2}, {animals: 1, _id:0}, {limit: 3})
Unfortunatelly it doesn't work like that and returns the whole object. Can anybody tell me how to do it?
For you problem you need the $slice projection operator instead of limit. The later limits the number of documents returned as a result of the query. Instead, the $slice operator is intended for exactly what you need.
Here is an example how to use it in your use case:
> db.getCollection('people').find({id: 2}, {_id: 0, animals: {$slice: [0, 3]}})
{
"id" : 2,
"name" : "Rob",
"animals" : [
"shark",
"snake",
"fish"
]
}

MongoDB Partial Unique Index on Object Field in Array Field

Lets say I have a collection whose objects have a structure like this:
{
"a": [
{"name": "foo", "meh": "whatever"},
{"name": "bar", "meh": "hem"}
],
"other_stuff": { }
}
It's possible for an object in this collection to not have the "a" field. I'd like to enforce a constraint on the database such that, if an object has the "a" field, that none of the "name" fields in objects contained in that "a" field are duplicates across the entire collection.
So for example, the following object would be flagged as a constraint violation if the above object were already in the collection:
{
"a": [
{"name": "bar", "meh": "meh meh"}
],
"other_stuff": { }
}
Furthermore, the following object would be flagged as a constraint violation regardless of other documents already in the collection:
{
"a": [
{"name": "boo", "meh": "blub"},
{"name": "boo", "meh": "glub"}
],
"other_stuff": { }
}
Is it possible to specify a partial unique index for MongoDB? If it's different between Mongo 3.2, 3.4, 3.6 and 4.0 it would be nice to know that too - I don't care about earlier than 3.2.
I was thinking it might be something like this to prevent duplication across documents (because in Javscript [] > '' === false and undefined > '' === false and ['anything'] > '' === true for the passive reader):
db.MyCollection.createIndex(
{ "a.name": 1 },
{
"unique": true,
"partialFilterExpression": {
"a": { "$gt": '' }
}
}
)
... and I think the only way to prevent duplicate values within a document is to enforce in application logic the use of the $addToSet operator when operating on the "a" field.
I'd be happy to be corrected or corroborated on either count.

spring-data-mongodb document design options - array vs dynamic field (i.e. map)

I am designing a document structure and use spring-data-mongodb to access it. The document structure is to store device profile. Each device contains modules of different types. The device can contains multiple modules of the same type. Module types are dynamic as new type of modules are created sometimes.
Please note: I try not to write custom queries to avoid boilerplate code. But, some custom queries should be fine.
I come out with two designs:
the first one use dynamic field (i.e. map). Semantics is better but seems harder to query/update using spring-data-mongodb.
{
deviceId: "12345",
instanceTypeMap: {
"type1": {
moduleMap: {
"1": {field1: "value",field2: "value"},
"2": {field1: "value",field2: "value"}
}
},
"type2": {
moduleMap: {
"30": {fielda: "value",fieldb: "value"},
"45": {fielda: "value",fieldb: "value"}
}
}
}
the second one use array and query/update seems more in-line with spring-data-mongodb.
{
deviceId: "12345",
allInstances: [
{
type: 1,
modules: [
{
id: 1,
field1: "value",
field2: "value"
},
{
id: 2,
field1: "value",
field2: "value"
}
]
},
{
type: 2,
modules: [
{
id: 30,
fielda: "value",
fieldb: "value"
},
{
id: 45,
fielda: "value",
fieldb: "value"
}
]
}
]
}
I am inclined to use array. Is it better to use array instead of dynamic field with spring-data-mongodb. I did some search on-line and found people mentioned that query for key (i.e. in map) is not as easy in spring-data-mongodb. Is that a correct statement? Do I miss anything? Thank you in advance.
I ended up with the design as below. I use one device-instance-type per document. Because, in some scenario,
updates are done on many modules of the same instance type. Those updates can be aggregated as just one database update.
The redundant "moduleId" field is also added for query purpose.
{
deviceId: "12345",
instanceTypeId: "type1",
moduleMap: {
"1": {
moduleId: "1",
field1: "value",
field2: "value"
},
"2": {
moduleId: "2",
field1: "value",
field2: "value"
}
}
}
Now, I can use spring-data-mongodb's query:
findByDeviceId("12345");
findByDeviceIdAndInstanceTypeId("12345","type1");
findByDeviceIdAndInstanceTypeIdAndModuleMapModuleId("12345","type1","1");

Updating nested objects in MongoDB

I am trying to update a nested document in MongoDB that looks similar to below (shortened to be concise).
{
"cols": "20",
"saveTime": "2014-06-15-10:44:09",
"rows": "20",
"gameID" : "66f2497c-7a2b-4210-a06b-80be0e6a8fd8",
"players": [
{
"id": "Inhuman",
"num": "1",
"color": "00ff00ff",
"name": "badComputer",
"type": "1"
},
<...>
],
"nodes": [
{
"g": "1",
"c": "0",
"r": "0",
"a": "0",
"o": ""
},
{
"g": "1",
"c": "0",
"r": "1",
"a": "0",
"o": ""
}
<...>
],
}
What I am trying to do is update one of the nodes. For example, I want to change the node:
{ "g": "1", "c": "0", "r": "0", "a": "0", "o": ""}
to
{ "g": "1", "c": "0", "r": "0", "a": "5", "o": ""}
I have tried using the dot (.) notation, with the $set command, ala:
db.game.update({"gameID" : "66f2497c-7a2b-4210-a06b-80be0e6a8fd8"}, { $set: {"nodes.r":"0", "nodes.c":"0", "nodes.a":"5"}}),
But that does not give me the expected behavior because I'm updating all nodes with the same r and c values. This is obviously not what I want, but I do not see how to update a specific piece of this document. Does anyone have any idea how to do this?
If you are looking to update a specific item in your "nodes" array that you do not know the position of but you know the "criteria" to match that item, then you need the $elemMatch operator along with the positional $ operator in the update side:
db.game.update(
{
"gameID" : "66f2497c-7a2b-4210-a06b-80be0e6a8fd8",
"nodes": { "$elemMatch": { "g": 1, "r": 0 } }
},
{ "$set": { "nodes.$.c":"0", "nodes.$.a":"5" } }
)
The positional $ operator contains the matched "index" position of the first element "matched" by your query conditions. If you do not use $elemMatch and use the "dot notation" form instead, then the match is only valid for the whole document containing values that would be true and does not reflect the "position" of the element that matches both of the field conditions.
Care must be taken that this is the "first" match, and typically expected as the only match. The reason being that the positional operator will only contain the "first" position where there were multiple matches. To update more than one array item matching the criteria in this way, then you need to issue the update several times until the document is no longer modified.
For a "known" position you can always directly use the "dot notation" form, including the position of the element that you wish to update:
db.game.update(
{
"gameID" : "66f2497c-7a2b-4210-a06b-80be0e6a8fd8",
},
{ "$set": { "nodes.0.c":"0", "nodes.0.a":"5" } }
)
So the index position is 0 for the first element, 1 for the second element and so on.
Noting that in both cases, you only need to pass to $set the fields you actually want to change. So unless you are unsure of a value being present ( which would not be the case if that was your query ) then you do not need to "set" fields to the value they already contain.
To update specific node - you would need to put that in the query part of your search.
As in
db.game.update({"gameID" : "66f2497c-7a2b-4210-a06b-80be0e6a8fd8","nodes.r":"0",
"nodes.c":"0", "nodes.a":"5" }, { $set: {"nodes.$.r":"0", "nodes.$.c":"0", "nodes.$.a":"5"}})
You see the $ sign takes the node object it found that matches the first part (query) of the call, and sends you there in the second part (projection) part of your call.
Also check out this question