Taking my first shot at NoSQL development using RavenDB.
One thing that is confusing me is how we should handle duplicates across documents that are stored.
Let's say I have a Story (think BDD/ATDD):
{
Id: 10,
Title: 'Story 1'
}
Then I decide that a story can have one or more sub-stories. I then create another story and make then above story a sub-story.
{
Id: 15,
Title: 'Title',
Stories: [ { Id: 10, Title: 'Story 1' }]
}
Now I've got two stories. If I load the story with ID=15 and proceed to edit the first story in the Stories array, let's say, with a new title of 'Sub-story 1', the original story stays the same.
What I'm trying to understand: Do I care? If I do, what is the best way to make sure that everything stays in sync? When the sub-story is changed, I want the original story to change as well. What is the appropriate design/solution for this kind of thing?
The quick answer is that you shouldn't model things like that.
See: http://ayende.com/blog/156353/entities-associations-point-in-time-vs-current-associations
Related
I have this:
var map = new mapboxgl.Map
({
container: 'map',
style: 'mapbox://styles/mapbox/satellite-streets-v11',
});
It works. But I also want elevation/fog added to it. Reading the manual for many hours, it seems to me that you either have to specify a URL, like I have done, to a "pre-made style", OR specify an absolutely massive style object full of cryptic and incomprehensible sub-objects such as specific layers and stuff.
There doesn't seem to be a way to simply use that style "template" URL, and add elevation/fog on top of it. I really hope that I'm wrong about this.
Downloading the JSON object representing their "pre-made style", I was presented with a massive ocean of data, impossible to manage in any sensible manner. They cannot possibly mean that you should try to edit/adapt/extend that.
I must be missing something. Is there really no way to do what I do now, but enable the "terrain" and "fog" features?
https://docs.mapbox.com/mapbox-gl-js/style-spec/terrain/
https://docs.mapbox.com/mapbox-gl-js/style-spec/fog/
Naturally, I tried adding them to the Map object at first, assuming this was how it was done, but it was just ignored.
Again, if I were to make my own style object, it would be required to contain all sorts of scary and incomprehensible stuff: https://docs.mapbox.com/mapbox-gl-js/style-spec/root/
To be frank, I have no idea who would ever want a 3D map which can support elevation/height differences and realistic fog/lights, yet not use those features. To me, this seems like it would be a simple boolean setting that you turn on/off, and which is enabled by default.
I have not tried this, but you should be able to set fog dynamically like this:
var map = new mapboxgl.Map
({
container: 'map',
style: 'mapbox://styles/mapbox/satellite-streets-v11',
});
map.on('load', () => {
map.setStyle({
...map.getStyle(),
"fog": {
"range": [-0.5, 3],
"color": "white",
"horizon-blend": 0.1
}
})
});
See the documentation here.
I'm working on a parent component, with conditionally rendered children components. I am simplifying the parent component a bit to help clarify my issue.
Display 1: Display list of "Courses"
The page starts off displaying a list of the user's "Courses"; a "Course" is just a collection inside Firestore. To access these collections, I call the "FirestoreConnect()" at the export default line of the end of this parent component.
However, once the user clicks on one of the Courses that are displayed, the screen enters Display 2.
Display 2: Display list of "Lectures"
Once the user chooses a course, I want it to display a list of that courses "lectures". "Lectures" are simply Firestore sub-collection within that specific courses collection. However, since I already called the "FirestoreConnect()" once already during the initial rendering of this parent, I don't know how to recall it specifically to read the selected sub-collections material. (Since I didn't know before hand which course the user would choose.)
Is there a way to continue recalling a FirestoreConnect() multiple times, for different collections, inside the same parent component?
Since we don't see your code, I will be guessing how you are passing the parameters for your firestoreConnect(). I'm saying that to clarify that you need to pass the subcollection/subdocument hierarchy information as to firestoreConnect(). So, your code would be something like below:
const enhance = compose(
firestoreConnect(props => {
return [{ collection: "Courses", doc: props.uid, subcollections: [{ collection: "Lectures" }], storeAs: `${props.uid}-lectures` }];
}),
connect(({ firestore }, props) => {
return {
lectures: firestore.ordered[`${props.uid}-lectures`] || []
};
})
);
...
const Lectures = ({ firestore, lectures }) => {
return <YOUR_LECTURES_LIST_UI>
}
While this code is untested, it was based from this tutorial here and I believe it might help you. You just need to know that the subcollection is still referenced in your firestoreConnect(), so the application already has this part of the collection configured.
In case this still doesn't help you, I would recommend you to check this other similar cases below, because they might help you as well.
Choose firestore subcollection when connecting component to redux with react-redux-firebase
How to load firestore subcollection to redux store?
Let me know if the information helped you!
I'm a couple hours new to Meteor and Mongo, coming from a Rails background and trying to understand how migrations work - or don't maybe?
I have a server/bootstrap.js file that I use to seed some data:
// if the database is empty on server start, create some sample data.
Meteor.startup(function () {
if (Users.find().count() === 0) {
var userData = [
{ name: 'Cool guy' },
{ name: 'Other dude' }
];
for (var i = 0; userData.length; i++) {
var userId = Users.insert({
name: userData[i].name
});
}
}
});
It seems like every time I want to change the database, say to add a new field, I have to run meteor reset to get it to pick up the changes.
But what happens if I create records or other data through the UI that I want to keep? In Rails, working with MySQL or PostgreSQL, I'd create a migration to create new fields without blowing away the entire database.
How does this work with Meteor and Mongo? Also thinking of the case of rolling out new changes from development to production. Thanks!
-- Update: 2013/09/24 --
Apparently, the schema-less nature of Mongo reduces or eliminates the need for migrations. In my case, modifying userData to add new fields won't work after it runs initially because of the Users count check - which is why I kept running meteor reset. I'll need to rethink my approach here and study up.
That said, there are projects out there that use migrations, like Telescope: https://github.com/SachaG/Telescope/blob/master/server/migrations.js
I also found the tutorial at http://try.mongodb.org/ useful.
First of all, your code is perfectly valid. And you know that.
mrt reset gives you a 'fresh' - empty database (as mentionned already).
If you want to reset a particular collection, you can do it so :
MyCollection.remove({});
But you have to understand the nature of NoSQL : there are no constraints on the data. It could be called NoREL (as in not a relational database, source : Wikipedia ).
MongoDB is also schema-less.
This means that you can use any field you want in your data. This is up to you (the programmer) to enforce specific constraints if you want some. In other words, there is no logic on the mongo side. It should accept any data you throw at it, just like Hubert OG demonstrated. Your code snippet could be :
// if the database is empty on server start, create some sample data.
Meteor.startup(function () {
if (Users.find().count() === 0) {
var userData = [
{ name: 'Cool guy' },
{ name: 'Other dude' },
{ nickname: 'Yet another dude' } // this line shows that mongo takes what you throw him
];
for (var i = 0; userData.length; i++) {
var userId = Users.insert({
name: userData[i].name
});
}
}
});
Source : http://www.mongodb.com/nosql
There is no need for migration there. You only have to add the logic in your application code.
Note : To import/export a database, you can have a look there : mongo import/export doc, and maybe at the db.copyDatabase(origin, destination, hostname) function.
There are no migrations in Mongo — there is no scheme! If you want to add a new field that was not there before, just do it and it will work. You can even have completely different documents in the same collection!
Items.insert({name: "keyboard", type: "input", interface: "usb"});
Items.insert({cherries: true, count: 5, unit: "buckets", taste: "awesome"});
This will just work. One of main reasons to use NoSQL (and advantages of Meteor over Rails) is that you don't have migrations to worry about.
Using mrt reset to change db model is a terrible idea. What it actually does is complete reset of db — it removes all of your data! While it's sometimes usefull in development, I bet it's not what you want in this case.
I am very new to Joomla (frankly just started exploring the possibility of using Joomla) and need help with programmatically adding articles to Joomla backend tables (please see details below). Also along the same lines, I would like to understand how should values for the columns:
parent_id
lft
rgt
level
be generated for the table jos_assets (#__assets) and what is their functional role (eg are they “pointers/indexes” analogous to, say, an os inode to uniquely indentify a file or are they more functional attributes such as identifying the category, subcategory etc)
It might help to use the following SIMPLIFIED example to illustrate what I am trying to do. Say we have a program that collects various key information such as names of the authors of web articles, the subject type of the articles, the date of articles as well as a link to the article. I want to be able to extend this program to programmatically store this information in Joomla. Currently this information is stored in a custom table and the user, through a custom php web page, can use search criteria say by author name, over a certain range of dates to find the article(s) of interest. The result of this search is then displayed along with a hyperlink to the actual article. The articles are stored locally on the web server and are not external links. The portion of the hyperlink stored in the custom table includes the relative path of the physical document (relative to the web root), so for example:
Author date type html_file
Tom 08-14-2011 WEB /tech/11200/ar_324.html
Jim 05-20-2010 IND /tech/42350/ar_985.html
etc.
With all the advantages that Joomla offers over writing custom php search and presentation pages as well as trending etc, we would really like to switch to it. It seems that among other tables for example that #__assets and #__content can be populated programmatically to populate Joomla from our existing php program (which is used to compile the data) and then use Joomla.
Any examples, suggestions and help is greatly appreciated
Kindest regards
Gar
Just an initial note: Joomla 1.6/1.7 are pretty similar. 1.5 not so much. I'll assume 1.6/1.7, as that's what I'd recommend as a base for a new project.
First up, you'll need to be running with access to the Joomla framework. You could do this through a Component, or a module, or a cron that bootstraps it or whatever. I won't go though how to do that.
But once you do that, creating an article is reasonably simple.
<?php
require_once JPATH_ADMINISTRATOR . '/components/com_content/models/article.php';
$new_article = new ContentModelArticle();
$data = array(
'catid' => CATEGORY_ID,
'title' => 'SOME TITLE',
'introtext' => 'SOME TEXT',
'fulltext' => 'SOME TEXT',
'state' => 1,
);
$new_article->save($data);
The actual list of fields will be a bit longer than that (required fields etc), but you should get sane error messages etc from the Joomla framework which illuminate that.
So in summary:
Load up the Joomla framework so you have access to the DB, components, models, etc
Include the com_content article class, which will handle validation, saving to the database etc for you
Create an article instance with the required fields filled in as appropriate
Call save()
Now that I think about it, that'll probably work in 1.5...
Found a better way to do this without any errors Create a Joomla! Article Programatically
$table = JTable::getInstance('Content', 'JTable', array());
$data = array(
'catid' => 1,
'title' => 'SOME TITLE',
'introtext' => 'SOME TEXT',
'fulltext' => 'SOME TEXT',
'state' => 1,
);
// Bind data
if (!$table->bind($data))
{
$this->setError($table->getError());
return false;
}
// Check the data.
if (!$table->check())
{
$this->setError($table->getError());
return false;
}
// Store the data.
if (!$table->store())
{
$this->setError($table->getError());
return false;
}
I'm just getting into facebook now. That "people you may know" section that pops up is driving me to distraction, and I'm just trying to get rid of it.
I have a greasemonkey script but its not working. None of numerous posted Greasemonkey scripts to remove it work either.
In the source for the facebook page , the "People You May know" (PYMK) section is in a script element that contains just one very long function call, i.e.
<script>big_pipe.onPageletArrive({"phase":1,"id":"pagelet_ego_pane","is_last":true,"css":["30YXW","MA+x5"],"js":["uBXoU","LNwoY","NavLF","ZtuLL"],"onload":["window.__UIControllerRegistry[\"c4dc09ff06275f0732488206\"] = new UIPagelet(\"c4dc09ff06275f0732488206\",
...
So I'm just trying to remove that script element and I tried this as a first pass:
function Remove_PYMK() {
var scripts = document.getElementsByTagName('script');
for (n=0; n<scripts.length; n++) {
if (scripts[n].innerHTML.indexOf("People You M") >=0) {
scripts[n].style.visibility="hidden";
alert ("found it");
}
}
}
Remove_PYMK();
//document.addEventListener("onload", Remove_PYMK, true);
It finds it, it just doesn't remove it.
This is something of a kludge, needless to say. Is there a proper way to disable PYMK, and ALSO, prevent my name from showing up in PYMK on other people's page.
EDIT:
Here's the entire script node up until the string "People You May Know" (FWIW):
<script>
big_pipe.onPageletArrive({
"phase": 1,
"id": "pagelet_ego_pane",
"is_last": true,
"css": ["30YXW", "MA+x5"],
"js": ["uBXoU", "LNwoY", "NavLF", "ZtuLL"],
"onload": ["window.__UIControllerRegistry[\"c4dc09ff06275f0732488206\"] = new UIPagelet(\"c4dc09ff06275f0732488206\", \"\\\/pagelet\\\/generic.php\\\/WebEgoPane\\\/\", {}, {});; ;", "new EmuController(\"6002939839588-id_4dc09ff0611f60c83869379\", \"AQBsgeDBRnJ4uiUuieZs681Fm3aqB5-626iHBbItdNfF5TWZZJ-9dZ60xyVaJr7JfimJpKgKNxXqF_DH_aHjru3RItUcHs2gI_ZLQNYdIENRSM5zh_pcjiDZyN7vv0trj5UDBOfXp6A6yDdVCZDLntgw1fU3cFSjPQkk9KQHnU3B61bkLQfoyQzysiefB82Ptf7tgkHDqlDwNpUT-HhYPOr8yGKxD6XavtgIfsW9hLPfGy6Eg7POsGHDyXrIDrl-Q3HM2CPoByeL43H0wIklCdbe8Oi3VnfKa-ysGjLB8YuAZHOJ1GH9feOxFphmcHE5C1R15rcPdnXSOaBI5bIJdJ24pIfAlNmGjXSMHU5LOiBm3FvCi_WzotJKxeRPjMBRmqQXw2CJ-xhFGGFqGRfJvoa9m8XKb1YpXzx-pqcpzDJ1z2xz3TT7gvObi5U-9ZaRtzNHY5g0UI3W-JeYjC-67Ir74mltDpXojdx-fWG5c-3OoX5bNJrCh1ifbQKFn3wLDAMdUHk4h8GO5eJdP_1xvJFal0SWZnwnMbty2AAd7EeFcSQNkjdK9BViqFo7OLLh8tT-j9k1fNnccza4M9jN94NEEjXRxU2KYhSGjdKL0fMJCfYA1y-3MMi7WbwEg7QiH-0AY_vQCu75j7vBtrwhjVpIK4kpBpNLYx5ucqLMe1RXt3PkE-xY0Jr5LRtwDq6MjeLrCjP0JaBL9o55o2DGPN-MNndM5YSCiZbri6ms7kJ0DADuZxiWkZDISrZlE_RZ4-8\", 4, 0)", "new EmuController(\"6003087448525-id_4dc09ff0612184c51197177\", \"AQAVDQezDWg8RCTH6EnPLCILHIX4upfP5v0NJ_-npYstbN0e4SWpCrfbYAQRM_9dVfozxS89XSxUt1jgEQpRbTHE3sfkcngkwyKIjKGhmIS8rKfDX_gP4ER0NnVJlJyX6FaGRul0h2UUYxnxj3uaIsq7LzcI36r4lBTXBg1THtRC2pZ1MuUzvkTlYlwh4B-c5lXwt-6nKTTMdoZsTRsUDG_JTAPHeTV0-FYMr-1roaJpR_j--aw_MxJ6NxFEbTvkyL4QVN-ZRpWfeovChdYk4j_cgxoxRE-qm-u_hYcVbbtVrQXq0kVtHHhX8ijyeLt-4kZcM6gfnHpfBkB06z69adcA9rzWLA6VEo4_OFthPtc2SydkBs6DyAx51mAG3mBgnXXBJciYrQzWM-vSvODcV1dKTb5tSxO81Uswl-3Uo9Jf_VzfUgoKdeHyQeem31WMUmYm9kxCOVvKy4p7sE09GQoAfPBoz4MOrAgs3MFBFzpwIvKyZGvE6cX8haiK4POdP1zrEnMLFDyF0HRAaxfl4gKYw9kpGOtkrwezzU_biPYxV26-sUjXvPGx87WAZszqlIosXOJVux33eJD3V5bDcFD06Faze9OE5Segl4Tdmd0VZs73lt4PGXfF90QLCqZeUuNUUCkhRRRsP99SdW99Ghx0JncIQDpfX3fByce58jmKSCFWjcHy5qTPlryVI3KVow-XMLiguVmdbicCBGq-7im00hCmjH2-5HCuQnhHafNHUSZzZiHTg4KaTqvIMTfQIXs\", 4, 0)", "new EmuController(\"6003788660296-id_4dc09ff0612299683640305\", \"AQCvUWOfDiphu5NGe7r6dBna_IEvTbjrshIpxmOlMO3jwk650AHb_T03II0mnzLwpdy617kQMD8n8RIE_YqIE1hNxxoZL4M4ba8QiPz6h-u003c\/h4>\JW8WSPFBt1RSMeJQV7swF6HWeAm3Ew0fphxMwpdYDIu2fbEGLujVFCKwtMCzN6As6berB8z7iOoWdsAFulXLBLvHID0-Dn0_qCxF-AKl0b9s8cWBwF9fG1oI0r1TNQZGILLW8daCOkkjhpCUOHkXI6PFxyw5CXYLSmnVAxpvougwh7vt-NV3RWPuYnLN1QOvf07q2DRv-2wY1JUexmcWl6f5zquDJfEW3CxlluJ76eKZowfiGW-V-1M9K9whgfALkEjGDA-JM9P_lfXaukcmDDwAIHLFwG_7a9jNP4XmbLUyQEhUesxkImM5z7IAzt-HBIbtDfD8kHt50AG3nm3GEHv-yChqKMWqmcuzyYgmMGaXXKCd9Pb2a_pcF-QR_YIg2qgptFnn82D7Bb9Tedmce-FveNix9Ej7_fwr-IAncYwABNrs9zX34TkbLgvqhvH9RayLDXO7GnGWB_I4jorTbmNvpxf0rG8aXp3udXdNTEZAjmU3Yp1TOySAXY2Z5Ju6oYS51-tQ2SZIihYsa49TxQdSHet9vO4tjY_7rwT8Cou_TrmFWwZXuQTGwfUByP9Cl6wrUNhHeW3zbrB-BAxofxqcGQ44C7HPsvVTdkSOKhx60zaWaaBnDZ87QjEAgNJ-NjN8EqO_jMq3ig2Vn2CKzBhhQ\", 4, 0)", "new EmuController(\"6002985380734-id_4dc09ff06123a8641112115\", \"AQBkHx98vGQ8Bn2h8R42L-S1OQfGO1ksTR41Mds3V7wEwzKNgBG4H2o-EHRvRSd9SsIPGChah-NHU5Jt_wbtng06_Dw0U0WkxfBkFx374OINZj69o69sPYmoQZlY2xsClxS0T2t_h7zs9RzX4NpvYtSL6I3MALL0CsWS0KLv-CQHUF-IWpeNj6uGLG4efTFLBBMeQM0-Wr1IWYXU6oQZJRPwS7trOw4xW56wGBnhGPv1KRwr8ITU4NGUZlBKrUNRvx1SeyFn4MYNCrzXG0W_HxsWWv_ekTeDefvMF0gHZNDgcA07-FLEYxfCEzBL498OERPhoyBsmFE9fGbOY1EcO_LB_moqIncJF-TosooObw7f4BMZwHMxfGgju_UKndyeRq_l3pcTkSyjhbZY5-1sjojM8AI9VxCLdQms5QA7lvflmdPpwXS3b8UmCNfEtIy4iGED7yL7p1CKAKgJcO4jzmJ3Epk9tK38zy3Nv5yGB0-63dwjcvZMoTJMa545d2Vgs7J5_OiD6gA5fX4oB84m1dcI5e2G6AgzXAsWTUSuIGliPQr2oFjaz2mOzepyCvoatPjr1rtDkX_xNK9S27jXSuu9k0tiKCOkPsdccPO6235zCT0HeiPI0sHsUTpDmiFXHyFx0r_Kus5iCU5CjKjX1okSuKQL7WM5HTBvYRCAbPNthqa9oNDDNmwNG-Im7wNZHEw4ZZuXpqg4UolyUZAkY7dKBRvGmK0sSGSIjIISOq1e6zBdekf7OlZ5yLnCwhAI_5E\", 4, 0)"],
"onafterload": ["new EmuTracker(\"6002939839588-id_4dc09ff0611f60c83869379\", false)", "new EmuTracker(\"6003087448525-id_4dc09ff0612184c51197177\", false)", "new EmuTracker(\"6003788660296-id_4dc09ff0612299683640305\", false)", "new EmuTracker(\"6002985380734-id_4dc09ff06123a8641112115\", false)"],
"refresh_pagelets": ["c4dc09ff06275f0732488206"],
"content": {
"pagelet_ego_pane": "\u003cdiv id=\"c4dc09ff06275f0732488206\">\u003cdiv class=\"ego_column egoOrganicColumn\">\u003cdiv class=\"ego_section\">\u003cdiv class=\"uiHeader uiHeaderTopAndBottomBorder mbs uiSideHeader\">\u003cdiv class=\"clearfix uiHeaderTop\">\u003ca class=\"uiHeaderActions rfloat\" href=\"\/l.php?u=\u00252Ffind-friends\u00252Fbrowser\u00252F\u00253Fref\u00253Dpsa&h=1ef37&cb=3&p=AQCicK3gLdJTaaW2xZUw3DWp7N89P5QwZvSEfn3y2vOqjXyzr60nDcHEPY7Y9x7LpK9-J5evLB7_Dwm9F4d0k5i9sQ-CXiH_GXc-toIfka4dUSK_\">See All\u003c\/a>\u003cdiv>\u003ch4 class=\"uiHeaderTitle\">People You May Know\u003c\/div>
That GM script is not removing the script node, it is just "hiding" it -- which would never have an effect.
It is possible for GM to actually remove that script node but, alas, this will not happen before the node has run. Deleting it after that will not stop the operation, as the JS is already loaded into memory.
The easiest thing to do is to delete the container that is displaying the PYMK cruft.
I don't use Facebook, but from your code it appears that the container might have the id, "pagelet_ego_pane". In which case, you could delete it like:
var PYMK_Container = document.getElementById ('pagelet_ego_pane');
if (PYMK_Container) PYMK_Container.parentNode.removeChild (PYMK_Container);
You could also try to delete the annoying function, which may or may not be possible. The function appears to be set with that new UIPagelet(\"c4dc09ff06275f0732488206\" code. So, if you can find the corresponding function, you can try deleting it with:
unsafeWindow.OffendingFunctionName = function() { return; }
If that works, it may cut down on the JavaScript "churn" that might slow down that page.
As for stopping your name from showing up in other people's PYMK, that is something that is done by the Facebook servers, there may be a user setting that they have, otherwise you'll have to send Facebook a "sternly worded letter". ;)
A simpler way to do it would be to just add a user style sheet that hides the offending nodes, no javascript necessary.
However if you want a simple method that just works "out of the box" for hiding the "People You May Know" box, the FB Purity browser addon (also available as a greasemonkey script ) has an option for hiding the PYMK box, along with options for hiding most other boxes / links on the site.