Choosing document models in MongoDB - mongodb

Which of these document models would be better to use in MongoDB?
Animal:
{
_id: "1",
name: "abc",
locations_spotted: [
{
locId: "1",
dates: ["1-1-2009", "12-4-2013"...]
},
{
locId: "2",
dates: ["3-1-2012", "12-3-2013"...]
}
...
]
}
Animal:
{
_id: "1",
name: "abc",
loc1spotdates: ["1-1-2009", "12-4-2013"...],
loc2spotdates: ["3-1-2012", "12-3-2013"...],
...
...
}
There are a limited number of locations and only a few might get added in the future.

First variant is better in my opinion. Because in future if you prefer to add additional information you add it right into the object inside array. You leave your options open. In future you could add time of the day also maybe.

Related

How to search a Mongo collection for a document that contains an array that contains an element that meets a particular criteria?

I'm new at Mongo and there might be a better way to do what I want. I'm dealing with a particular data structure that my application must process.
Suppose that I have a collection that contains two documents that contain information about universities and their student clubs to include the name of each club and the name of each student in each club along with their age:
{
_id: 1, // object ID
name: "Oxford University",
clubs: [{
name: "Soccer",
members: [{
name: "Alice",
age: 22
},
{
name: "Bob",
age: 23
}
]
}, {
name: "Gymnastics",
members: [{
name: "Charlie",
age: 20
},
{
name: "Dorothy",
age: 19
}
]
}]
}
{
_id: 2, // object ID
name: "Cambridge University",
clubs: [{
name: "Chess",
members: [{
name: "Ellen",
age: 30
},
{
name: "Frank",
age: 35
}
]
}, {
name: "Go",
members: [{
name: "Gilbert",
age: 25
},
{
name: "Hikari",
age: 40
}
]
}]
}
Suppose that I want to write a query on this collection that will find universities that have a club that has at least one member aged 40 or older.
How do I do that?
I sketched this example based off of the idea of taking some JSON documents and inserting them into a new collection. Maybe it would be a better idea to break this apart into multiple collections. I just had the idea to research if Mongo might be a good product to use in this situation given that a big part of my job here is to create something that can receive some JSON data, process it, and make it queryable.
MongoDB queries have a convenient feature to query documents that have a specific value or condition in embedded objects and arrays. In the query you can specify the "path" to the object or array using "dot notation" without having to specify the exact array index, etc.
Using your example, you can find the documents where a member of a club is aged 40 or older like this.
db.collection.find({
"clubs.members.age": {
"$gte": 40
}
})
This returns the second document in your example collection.
Try it on mongoplayground.net.

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");

Pushing data into nested arrays in MongoDB

Whats the best way of pushing data into an array that's nested several levels deep in arrays...
Here's an example document ( made up ) that's representative of the data in the document, except the real document has more schools with more classes and more students. Each 'thing' has a unique id :-
{
_id: 4353467865,
school : [
{
_id: 3425353,
name: 'school of rock',
class: [
{
_id: 3242342
name: 'Room1',
students: [
{ _id: 345456562, name: 'Kevin' }
]
},
{
// more classes with more students nested underneath
}
]
},
{
// more schools, classes, students...
}
]
}
Now I know the id of the document, school and class, and now I want to do an update $push into the students array a {_id: 234554363, name: 'Barry'} for that class.
How do I construct an update query for that?
Do the below 2 links solve your problem?
http://docs.mongodb.org/manual/reference/operator/update/positional/#nested-arrays
Update multi nested array in Mongodb
I'll answer my own question since I've been using Mongo for a while now.
first answer...
When designing Mongo stuff, I was super paranoid to try and get things in one doc if I could since that's the whole benefit. Reality is you don't have to be too paranoid. School and Class could be separate collections. It does depend a bit on how you are using them, but for the most I would do separate collections these days.
second answer...
I have needed to do nested arrays, however any level of nesting array can be separated out into non nested arrays with keys which is then trivial to update using mongo
so
school : [
{
_id: 3425353,
name: 'school of rock',
}
],
classes: [
{
_id: 3242342
school_id: 3425353,
name: 'Room1',
}
],
students: [
{ _id: 345456562, class_id: 3242342, name: 'Kevin' }
{ _id: 345456563, class_id: 3242342, name: 'Gilbert' }
]

MongoDB - $addToSet on a list of Embedded Document

I have a list of (mongodb) Embedded Documents within one Document and I am interested in adding a new embedded document to the list of the existing ones.
As far as I have researched, I can use $addToSet, what I can't figure out is how does MongoDB decide if the new document already exists in the list of embedded documents or if it's a new one, i.e. how does MongoDB decide if 2 embedded documents are equal?
p.s. the embedded documents I have are not just values, they are quite complex structures, so I was wondering if there is any place I can define what the equality between 2 of them means...
$addToSet uses the usual mongodb equality rules: it will do a deep value-by-value comparison, so the following two documents are identical:
{ name: "John", hobbies: ["coding", "drinking", "chess"] }
{ hobbies: ["coding", "drinking", "chess"], name: "John" }
(order within documents is not guaranteed, so they are identical)
while those aren't (pairwise):
// compare to:
{ name: "John", hobbies: ["chess", "coding", "drinking"] }
// in arrays, the order matters:
{ name: "John", hobbies: ["coding", "drinking", "chess"] }
// field names and values are case sensitive
{ Name: "John", hobbies: ["chess", "coding", "drinking"] }
{ name: "john", hobbies: ["chess", "coding", "drinking"] }
// additional field:
{ name: "John", lastName: "Doe", hobbies: ["chess", "coding", "drinking"] }
// missing field:
{ name: "John" }
Please note that there is no special field here. You can add an _id field, but it has no special semantics and will be treated just like any other field.

Mongodb, concat more updates in one query

There is a way to concat more updates?
For example I would like to change more values in the same element. So having this...
{
cc: [
{ user_id: "1", hasSeen:true}
,{ user_id: "2", hasSeen:false}
,{ user_id: "3", hasSeen:false}
]
,conversation: [{
user_id: "1",
text: "message by 1, to 2and3"
}]
}
...I would like to push a new conversation object and also change all the hasSeen values.
For do the first point, no problem, I just push only a new conversation object. And it works...
...update(
{ _id : _param.conversation_id }
,{ $push:{ conversation:{user_id:"2",text:"message by 2, to 1,3"} }}
)
.exec(function(err, numAffected, rawResponse) {
});
But I would like also to change the three "hasSeen" values in the same time. is it possible?
Can I do it with one query? or i should split it in two queries?
ps: I use mongoose.
Currently the positional operator (which I think you will need here) does not work in such a manner that you can do a type of conditional update whereby you iterate through a list of $incs or $sets to change those subdocument values.
There is a JIRA for such a thing that could possibly help you: https://jira.mongodb.org/browse/SERVER-6566?page=com.atlassian.jira.plugin.system.issuetabpanels:changehistory-tabpanel but it is filed under "features we are not sure of".
Best to split this up currently.