Querying by field in MongoDB, which can be arbitrarily deeply nested in the document - mongodb

I am currently developing a RESTful API/thinking about the implementation.
For the sake of simplicity, in my model, there's a document type, called 'Box'. A box can contain items, and other boxes as well. (Kind of like a composite pattern) Such sub-boxes can be arbitrarily deeply nested.
In MongoDB, such a box document would look like this:
{
_id: 0,
items: ['A', 'B', 'C'],
sub-boxes: [
{
_id: 1,
items: ['D', 'E']
},
{
_id: 2,
items: []
sub-boxes: [
{
_id: 3,
items: ['G']
}
]
}
]
}
My REST API url looks like this:
GET /api/boxes/:id
I would like to be able to retrieve the box #0 the same way as #3 (from an API point of view).
GET /api/boxes/0
GET /api/boxes/3
My question is that is it possible in MongoDB to query for the field _id, even if I don't know how deeply it is nested in the document? I cannot hardcode in my queries the location of _id, since it can be basically anywhere.
I know that I could normalize my model, so each 'sub-boxes' property would only contain references to other boxes, but I would prefer to keep my model denormalized, if possible.

In the end I decided to normalize my data, so each box only contains references to other boxes.

Related

Multiple arrays of objects inside array of objects in MongoDB

Fellow programmers.
Is it considered as a bad practice to use such MongoDb model:
{
companyId: '',
companyName: '',
companyDivisions: [
{
divisionId: '',
divisionName: '',
divisionDepartments: [
{
departmentId: '',
departmentName: ''
},
...
]
},
...
],
},
...
Because right now it's getting complicated to update certain departments.
Thanks.
I don't think this is a bad practice generally speaking. If your model resembles this data structure it is a good choice storing data this way, leveraging a document database. You can naturally handle data and most likely you have a direct map onto your data model.
Another choice would be to have three different collections:
companies;
divisions;
departements.
However, in this case you would end up storing data as you would do in a relational database. Thus, more than a general rule, it is a matter of data model and expected query profile on your database.
Edit: using MongoDb 3.6+
Using your document oriented approach, a single department can be granularly updated using the following update:
db.companies.findAndModify(
{
query: {
"companyId": "yourCompanyId"
},
update: {
$set : { "companyDivisions.$[element1].divisionDepartments.$[element2].divisioneName": "yourNewName" }
},
arrayFilters: [
{ "element1.divisionId": "yourDivisioneId" },
{ "element2.departmentId": "yourDepartementId" }
]
});
This update uses the new powerful filtered positional operator feature introduced by MongoDB v3.6. The $[<identifier>] syntax allows to select an array entry based on a specific condition expressed in the arrayFilters option of the db.collection.findAndModify() method.
Note that in case the condition matches multiple array items, the update affects all such items, thus allowing for multiple updates as well.
Furthermore, note that I would apply such an optimization only in case of need, since premature optimization is the root of all evil. (D. Knuth).

How can I model my meteor collection to feed three different reactive views

I am having some difficulty structuring my data so that I can benefit from reactivity in Meteor. Mainly nesting arrays of objects makes queries tricky.
The three main views I am trying to project this data onto are
waiter: shows order for one table, each persons meal (items nested, essentially what I have below)
kitchen manager: columns of orders by table (only needs table, note, and the items)
cook: columns of items, by category where started=true (only need item info)
Currently I have a meteor collection of order objects like this:
Order {
table: "1",
waiter: "neil",
note: "note from kitchen",
meals: [
{
seat: "1",
items: [ {n: "potato", category: "fryer", started: false },
{n: "water", category: "drink" }
]
},
{
seat: "2",
items: [ {n: "water", category: "drink" } ]
},
]
}
Is there any way to query inside the nested array and apply some projection, or do I need to look at an entirely different data model?
Assuming you're building this app for one restaurant, there shouldn't be many active orders at any given time—presumably you don't have thousands of tables. Even if you want to keep the orders in the database after they're served, you could add a field active to separate out the current ones.
Then your query is simple: activeOrders = Orders.find({active: true}).fetch(). The fetch returns an array, which you could loop through several times for each of your views, using nested if and for loops as necessary to dig down into the child objects. See also Underscore's _.pluck. You don't need to get everything right with some complicated Mongo query, and in fact your app will run faster if you query once and process the results however many times you need to.

How to store related records in mongodb?

I have a number of associated records such as below.
Parent records
{
_id:234,
title: "title1",
name: "name1",
association:"assoc1"
},
Child record
{
_id:21,
title: "child title",
name: "child name1",
},
I want to store such records into MongoDb. Can anyone help?
Regards.
Even MongoDB doesn't support joins, you can organize data in several different ways:
1) First of all, you can inline(or embed) related documents. This case is useful, if you have some hierarchy of document, e.g. post and comments. In this case you can like so:
{
_id: <post_id>,
title: 'asdf',
text: 'asdf asdf',
comments: [
{<comment #1>},
{<comment #2>},
...
]
}
In this case, all related data will be in the save document. You can fetch it by one query, but pushing new comments to post cause moving this document on disk, frequent updates will increase disk load and space usage.
2) referencing is other technique you can use: in each document, you can put special field that contains _id of parent/related object:
{
_id: 1,
type: 'post',
title: 'asdf',
text: 'asdf asdf'
},
{
_id:2
type: 'comment',
text: 'yep!',
parent_id: 1
}
In this case you store posts and comments in same collection, therefor you have to store additional field type. MongoDB doesn't support constraints or any other way to check data constancy. This means that if you delete post with _id=1, comments with _id=2 store broken link in parent_id.
You can separate posts from comments in different collections or even databases by using database references, see your driver documentation for more details.
Both solutions can store tree-structured date, but in different way.

Is it possible to query MongoDB by subfield object values?

I have documents like this:
db.planet.insert({
name: 'Earth',
stuff: {
'grass': 'green',
'sky': 'blue',
'sea': 'blue'
}
})
db.planet.insert({
name: 'Mars',
stuff: {
'dust': 'red',
'sky': 'yellow'
}
})
I want to find all planets that have at least some blue stuff (only earth in this case). How can I do that?
Catch: I tried solving this by using an array (instead of object) for stuff (stuff: [ { k: 'grass', v: 'green'},... ]) but I also need to be able to update (upsert to be exact) value of some stuff. For instance I must be able to do this:
db.planet.update({ name: 'Mars' }, {
'$set': {
'stuff.canals': 'brown',
'stuff.dust': 'reddish'
}
})
So, how can I find the planets with something blue on them? :)
Use the $or operator. The $or operator value should be an array, with the array containing conditions of which at least one must be true.
In your case, given the field names you have mentioned:
db.planet.find($or:[
{"stuff.sky":"blue"},
{"stuff.grass":"blue"},
{"stuff.sea":"blue"},
{"stuff.dust":"blue"},
{"stuff.canals":"blue")
])
See http://docs.mongodb.org/manual/reference/operator/or/
As there are a quite a number of different fields you want to query, you may want to keep an array of all these field names somewhere so you can generate the $or array programmatically.
I don't think there's a way to make the query without knowing the field names in advance. See mongodb query without field name .

MongoDB many-to-many search

this is my collections: (many-to-many)
actors:
{
_id: 1,
name: "Name 1"
}
movies:
{
_id: 1,
name: "The Terminator",
production_year: 1984,
actors: [
{
actors_id: 1,
role_id : 1
},
{
actors_id: 2,
role_id : 1
}
]
}
I can't get a list of actors for some movie
it is not a problem when I have this:
{
_id: 1,
name: "The Terminator",
production_year: 1984,
actors: [1,2,3,4,5] (actors id's)
}
var a = db.movies.findOne(name:"The Terminator").actors
db.actors.find({"_id":{$in:a}})
but, how can I make it with this above structure:
if, I do this var a = db.movies.findOne(name:"The Terminator").actors
it returns me this:
[
{
actors_id: 1,
role_id : 1
},
{
actors_id: 2,
role_id : 1
}
]
How do I get only this in array [1,2] (actors_id) to get the names of actors (with $in)
Thanks,
Zoran
You don't. Within MongoDB you always query for documents so you have to make sure your schema is such that you can get all the information you need by querying for specific documents. There is no join/view like functionality in MongoDB.
Denormalization is usually the most appropriate choice in such cases. Your schema looks like it's designed for a traditional relational database and you will have to try and let go of some of the schema design principles that come with relational data.
Specifically for your example you could add the actor name to the embedded array so you have that information after querying for the movie.
Finally, consider if you're using the right tool for what you need to do. Too often people think of MongoDB is a "fast MySQL" which is entirely wrong. Document databases are very different to RDBMS and even k/v stores. If you have a lot of related data use an RDBMS.
variable a in db.movies.findOne(name:"The Terminator").actors is an array of documents, so you'd have to make it an array of integers (ids)