This is my first time dealing with a NoSQL form of database and I'm a little confused about "relationships" in document-oriented databases. I'm using LoopBack & AngularJS.
I have a model page that has many page as it's children (i.e. menu items and submenus).
A page model is as follows:
"properties": {
"name": {
"type": "string",
"required": true
},
"slug": {
"type": "string"
},
"link": {
"type": "string"
},
"createdAt": {
"type": "date",
"required": true
},
"children": {
"type": [
"object"
]
}
},
with
"relations": {
"children": {
"type": "hasMany",
"model": "page",
"foreignKey": "parentId"
}
},
My confusion is that whenever I explore the LoopBack API, and then get the parent pages, I don't see the children property populated. But, doing a get to see the children of a parent (using the parent's id) turns out fine - I can see the parentId populated with it's parent.
My question is if this is normal when dealing with NoSQL/document-oriented databases, or am I doing something wrong?
Thanks a lot!
Sometimes LoopBack queries are getting messy when its come to relationships and parallel queries.
I think this is simply a query issue in LoopBack.
As I realized this is like Parent Page object can have multiple Children page objects.
We can use following query statement to retrieve related parent page and children pages.
app.models.Page.find({
where: {
parentId : 'yourPageId'
},
include: {
relation: 'Children'
}
}, function (err, pages) {
if (err) {
cb(err);
} else {
console.log("you will get related children pages alone with parent page")
cb(null, pages);
}
});
Hope this will work...!!
I believe you might be missing the other direction of the relationship.
You can do a belongsTo relation in the children to specify that it belongs to a parent.
That should enable you to see both directions' methods in the explorer.
Also, the foreingKey parentId should be set on the children, not on the parent. I.e. leave foreignKey empty on the parent relations definition, and instead use it on the children relations definition.
So on one hand, in the model you will have inside the relations field:
"children": {
"type": "hasMany",
"model": "page",
"foreignKey": ""
},
And also
"parent": {
"type": "belongsTo",
"model": "page",
"foreignKey": "parentId"
},
plus any other relations you have.
I have tested this and it works, although I have used two different models for the relation instead of only one.
I.e. I use
ModelA hasMany ModelB and ModelB belongsTo ModelA
instead of
ModelA hasMany ModelA and ModelA belongsTo ModelA
In mongoose when you want to get the page's "children" populated you need to specifically ask for it using:
Pages.find({})
.populate('children')
.exec(function(err, pages){
//..
})
Looking on LoopBack's page (http://loopback.io/doc/en/lb2/Querying-related-models.html) there is something similar called "include", you should try doing:
Page.find({include:'children'}, function() {
//...
});
Let me know if that worked!
Related
I have two questions about DTO implementation.
I have two apis return slightly different nested objects, which responses like:
{
"shop": {
"id": 1,
"name": "name",
"logo_url": "logo_url"
}
...
}
and
{
"shop": {
"id": 1,
"name": "name",
"logo_url": "logo_url",
"address": "...",
"phone": "..."
}
...
}
should I define shop as a inner class of each parent dto, or just define two different dto classes.
Some apis accept same dto object for both create and update, should I define just one class like EditShopDTO for both create and update, or define another two classes like CreateShopDTO and UpdateShopDTO.
Thank you for your help.
In my sailsjs app I have a data object that is returning a list of it's children objects. Now those children are returning complete attributes, is there a way for me to limit the children to return just one property (ex. the id)
currently i get something like this
{
"name": "parent",
"children": [
{
"id": "id456",
"name": "child",
"createdAt": "2015-09-09T03:10:42.000Z",
"dateAdded": "2014-10-10T04:00:00.000Z"
}
],
"id": "id123",
"createdAt": "2015-09-09T03:10:42.000Z",
"dateAdded": "2014-10-10T04:00:00.000Z"
}
I would like to get back this:
{
"name": "parent",
"children": [
"id456"
],
"id": "id123",
"createdAt": "2015-09-09T03:10:42.000Z",
"dateAdded": "2014-10-10T04:00:00.000Z"
}
Waterline does have select which can be used in a criteria object, but I haven't seen this work as a criteria object for .populate(). There is a bug with sails-mongo that makes select work incorrectly. It looks like this will be fixed shortly.
Anyways, if we assume that continues not to work, you could just map it:
Parent.find()
.populate('children')
.then(function(parent){
parent.children = _.map(parent.children, function(c){return c.id}))
})
It's kind of a pain but I have to do it often enough.
I'm trying to build a SAPUI5 application using JSON model.
I want to add 2 dropdown select menus, so that the second one fills in depending on the current selection of the first one.
I tried to get it done, but stucked at the very beginning.
This is what I get from Ajax (the object itself is passed from the server):
var data = {
"firm1":["firm1project1","firm1project2","firm1project3"],
"firm2":["firm2project1","firm2project2","firm2project3"],
"firm3":["firm3project1","firm3project2","firm3project3"],
"firm4":["firm4project1","firm4project2","firm4project3"]
};
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData(data);
sap.ui.getCore().setModel(oModel);
The first menu should be the list of firms and the second one - is the selected firm's projects. And now I have no idea how to bind the data to the controls the right way.
Thank you.
EDIT:
According to what I've read in the developer's guide, the data that should appear in different controls is contained under fixed keys (as shown in "Aggregation Data Binding in a Simple Application" part of the page that I linked above), so it would be possible to just write something like:
var oListItemTemplate = new sap.ui.commons.ListItem("ListItemTemplate", {
text : "{firm}",
});
and for the projects:
var oListItemTemplate = new sap.ui.commons.ListItem("ListItemTemplate", {
text : "{firm/project}",
});
But in my case there is no firm key. My ListItems for the firm selection menu are keys, not values. And ListItems for the project selection menu are in an array, not just separate values under fixed keys.
So, is there a way or to bind somehow the data I have in its current form to controls, or how the data should look like to be useful for binding?
your data looks better like the following, the data binding JSON objects are with keys. And there are two data models needed for two dropdown boxes, multi data model binding should be used. I did a example at JSBin which fulfills your requirements, you can have a look and get some thoughts.
{
"firms": [
{
"name": "firm1",
"projects": [
{
"name": "firm1project1"
},
{
"name": "firm1project2"
},
{
"name": "firm1project3"
}
]
},
{
"name": "firm2",
"projects": [
{
"name": "firm2project1"
},
{
"name": "firm2project2"
},
{
"name": "firm2project3"
}
]
},
{
"name": "firm3",
"projects": [
{
"name": "firm3project1"
},
{
"name": "firm3project2"
},
{
"name": "firm3project3"
}
]
},
{
"name": "firm4",
"projects": [
{
"name": "firm4project1"
},
{
"name": "firm4project2"
},
{
"name": "firm4project3"
}
]
}
]
}
I work on a Symfony project (an API) and it uses JSMSerializer, so every data sent as payload is in json format.
Then, I have two entities, say User and Devices. I have build two form types for each one, and while relationship between these two is OneToMany, in the first one's FormType, the 'devices' field is a collection of DeviceType forms.
When adding, is simple, you have to post something like this
{
"user": {
"name": "John",
"devices": [
{
"os": "Android",
"color": "red"
},
{
"os": "Android",
"color": "blue"
}
]
}
}
Everithing works just fine. Entities are persisted in db.
When updating, same payload can be sent, in that very same order, with some modified data, just like this
{
"user": {
"name": "John",
"devices": [
{
"os": "Android",
"color": "redish"
},
{
"os": "Android",
"color": "blueish"
}
]
}
}
and Symfony does its magic.
My problem is when order of the devices sent when updating changes. It causes all kind of problems.
My question is how can I send an ID of the elements in collection so symfony/doctrine maps it and knows who and how to update?
I thought of something like this
{
"user": {
"name": "John",
"devices": {
"2": {
"os": "Android",
"color": "blueish"
},
"1": {
"os": "Android",
"color": "redish"
}
}
}
}
but it does not work.
When you try to update an entity by persist, the instance you have currently is not attached to the entity manager. Then when you persist the entity, it creates another one.
If that appends, you need to add the id to the entity, and merge instead of persist.
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#merging-entities
Alternative solution:
If you are making a request to update devices order in a list, maybe you should just send an array with new order, such as array(id => newOrder, ...), and load all devices owned by an user, update their order, and flush them.
In this solution, the order must be an entity attribute, integer. If it is not done yet, it should be
I want to store website menus in Mongo for the navigation of my CMS, but since I'm new to Mongo and the concept of documents, I'm trying to figure out what would be best:
a) Should I store menu documents, containing children and those having more children, or
b) Should I store menu item documents with parent_id and child_ids ?
Both would appear to have benefits, since in case A it's normal to load an entire menu at once as you'll need everything to display, but B might be easier to update single items?
I'm using Spring data mongo.
PS: If I asked this question in a wrong way, please let me know. I'm sure this question can be expanded to any general parent-child relationship, but I was having trouble finding the right words.
Since menus are typically going to be very small (under 16MB I hope) then the embedded form should give you the best performance:
{
"topItem1": [
{ "name": "item1", "link": "linkValue" },
{ "name": "item2", "link": "linkValue" }
],
"topItem2": [
{ "name": "item1", "link": "linkValue" },
{ "name": "item2", "link": "linkValue" }
{
"name": "sub-menu",
"type": "sub",
"items": [
{ "name": "item1", "link": "linkValue" },
{ "name": "item2", "link": "linkValue" }
}
}
]
}
The only possible issue there is with updating the content inside nested arrays, as MngoDB can only "match" the first found array index. See the positional $ operator documentation for this.
But as long as you know the positions then this should not be a problem, using "dot notation" concepts:
db.menu.update({}, {
"$set": {
"topItem2.2.items.1": { "name": "item3", "link": "linkValue" }
}
})
But general adding should be simple:
db.menu.update(
{ "topItem2.name": "sub-menu" },
{
"$push": {
"topItem2.2.items": { "name": "item4", "link": "linkValue" }
}
}
)
So that is a perspective on how to use the inherrent embedded structure rather than associate "parent" and "child" items.
After long hard thinking I believe I would use:
{
_id: {},
submenu1: [
{label: "Whatever", url: "http://localhost/whatever"}
]
}
I thought about using related documents with IDs all sitting in a collection but then you would have to shoot off multiple queries to get the parent and its range, possibly even sub-sub ranges too. With this structure you have only one query for all.
This structure is not infallible however, if you change your menu items regularly you might start to notice fragmentation. You can remedy this a little with powerof2sizes allocation: http://docs.mongodb.org/manual/reference/command/collMod/#usePowerOf2Sizes
But yes, with careful planning you should be able to use one single document for every parent menu item