how to represent trees and their content in MySQL? - sql

I need to be able to store something like this:
where the green is a template type, the gray is a field container type, and the white is field. (That being, a field has a label and some text, both mediumtext for simplicity; and a field container can store other field containers or text, and a template is a top-level field.)
Now, let's say I want to allow users to create any number of these structures, but it is unlikely to be more than say 10. And, of course, we need to be able to link data to it.
This is all to be able to store in a database an associative array that looks for the above like, in pseudo code:
my_template = {
page_info => { description => 'hello!!!' },
id => 0,
content => { background_url => '12121.jpg', text => ...
}
Having an easy way to add a field to all data using the template when the template changes (say, we add a keywords to page_info) would be a big plus.
I can't figure this out at all, thanks a lot!

There are several different ways to store heirarchical data structures (trees) in MySQL. You can for example choose:
Adjacency list
Nested sets
Path enumeration
Closure table
See Bill Karwin's presentation for more details on the pros and cons of each.

Related

Error trying to reorder items within another list in Keystone 6

I'm using KeystoneJS v6. I'm trying to enable functionality which allow me to reorder the placement of images when used in another list. Currently i'm setting up the image list below, however I'm unable to set the defaultIsOrderable to true due to the error pasted.
KeystoneJS list:
Image: list({
fields: {
title: text({
validation: { isRequired: true },
isIndexed: 'unique',
isFilterable: true,
isOrderable: true,
}),
images: cloudinaryImage({
cloudinary: {
cloudName: process.env.CLOUDINARY_CLOUD_NAME,
apiKey: process.env.CLOUDINARY_API_KEY,
apiSecret: process.env.CLOUDINARY_API_SECRET,
folder: process.env.CLOUDINARY_API_FOLDER,
},
}),
},
defaultIsOrderable: true
}),
Error message:
The expected type comes from property 'defaultIsOrderable' which is declared here on type 'ListConfig<BaseListTypeInfo, BaseFields<BaseListTypeInfo>>'
Peeking at the definition of the field shows
defaultIsOrderable?: false | ((args: FilterOrderArgs<ListTypeInfo>) => MaybePromise<boolean>);
Looking at the schema API docs, the defaultIsOrderable lets you set:
[...] the default value to use for isOrderable for fields on this list.
You're trying to set this to true but, according to the relevant section of the field docs, the isOrderable field option already defaults to true.
I believe this is why the defaultIsOrderable type doesn't allow you to supply the true literal – doing so would be redundant.
So that explains the specific error your getting but I think you also may have misunderstood the purpose of the orderBy option.
The OrderBy Option
The field docs mention the two effects the field OrderBy option has:
If true (default), the GraphQL API and Admin UI will support ordering by this field.
Take, for example, your Image list above.
As the title field is "orderable", it is included in the list's orderBy GraphQL type (ImageOrderByInput).
When querying the list, you can order the results by the values in this field, like this:
query {
images (orderBy: [{ title: desc }]) {
id
title
images { publicUrl }
}
}
The GraphQL API docs have some details on this.
You can also use the field to order items when listing them in the Admin UI, either by clicking the column heading or selecting the field from the "sort" dropdown:
Note though, these features order items at runtime, by the values stored in orderable fields.
They don't allow an admin to "re-order" items in the Admin UI (unless you did so by changing the image titles in this case).
Specifying an Order
If you want to set the order of items within a list you'd need to store separate values in, for example, a displayOrder field like this:
Image: list({
fields: {
title: text({
validation: { isRequired: true },
isIndexed: 'unique',
isFilterable: true,
}),
displayOrder: integer(),
// ...
},
}),
Unfortunately Keystone doesn't yet give you a great way to manage this the Admin UI (ie. you can't "drag and drop" in the list view or anything like that). You need to edit each item individually to set the displayOrder values.
Ordering Within a Relationship
I notice your question says you're trying to "reorder the placement of images when used in another list" (emphasis mine).
In this case you're talking about relationships, which changes the problem somewhat. Some approaches are..
If the relationship is one-to-many, you can use the displayOrder: integer() solution shown above but the UX is worse again. You're still setting the order values against each item but not in the context of the relationship. However, querying based on these order values and setting them via the GraphQL API should be fairly straight forward.
If the relationship is many-to-many, it's similar but you can't store the "displayOrder" value in the Image list as any one image may be linked to multiple other items. You need to store the order info "with" the relationship itself. It's not trivial but my recent answer on storing additional values on a many-to-many relationship may point you in the right direction.
A third option is to not use the relationship field at all but to link items using the inline relationships functionality of the document field. This is a bit different to work with - easier to manage from the Admin UI but less powerful in GraphQL as you can't traverse the relationship as easily. However it does give you a way to manage a small, ordered set of related items in a many-to-many relationship.
You can save an ordered set of ids to a json field. This is similar to using a document field but a more manual.
Hopefully that clears up what's possible with the current "orderBy" functionality and relationship options. Which of these solutions is most appropriate depends heavily on the specifics of your project and use case.
Note too, there are plans to extend Keystone's functionality for sorting and reordering lists from both the DX and UX perspectives.
See "Sortable lists" on the Keystone roadmap.

Pull mass name list from database or in script?

I need to fill an input box with a first name and last name. The user will press "Randomize" and it will pull a random first and last name and fill the inputs.
My question is I'm not sure if I should put the names in tables (firstNames, lastNames) or just store them in a javascript file and pull straight from that.
I'm trying to follow the Single Responsibility Principle so I'm inclined to choose the former, but then I have two more models, two more seeders, two more tables, and probably a class to pull all that together. And then do I fill from a CSV file or just from a manually populated seeder? It seems like a lot of work and extra files for a 1-time use.
I know I'll get crap for this being an opinion based question but there is no one or where else to ask.
Also if you know of a place to ask these kind of questions that won't get me ripped apart I'd appreciate that.
I would suggest using the Faker PHP library. That way you wouldn't have to create extra tables, models, or have to worry about finding yourself fake data.
To install it in your project, simply add the dependency in your composer.json file. and run a composer update.
"require-dev": {
"fzaninotto/faker": "1.3.*#dev"
},
Then you can use it to create fake first and last names for you (in your controller most likely)
$faker = Faker\Factory::create();
$firstName = $faker->firstName;
$lastName = $faker->lastName;
Edit:
To add your own names you can either edit or override the name provider file located here.
I would like to suggest Fakerino a new fake generator PHP library, with a modern approach, easy to extend with custom data, custom fake data class, or pre-configured groups of data.
https://github.com/niklongstone/Fakerino
<?php
include ('../Fakerino/vendor/autoload.php');
use Fakerino\Fakerino;
$fakerino = Fakerino::create();
echo $fakerino->fake('Surname')->toJson(); //["Donovan"]
echo $fakerino->fake('NameFemale'); //Alice
//with configuration
$fakerino = Fakerino::create('./conf.php');
print_r($fakerino->fake('fake1')->toArray());
/*
Array(
[0] => Arthur
[1] => Doyle
)
*/
//conf.php
<?php
$conf['fake'] = array(
'fake1' => array('NameMale', 'Surname' => null),
'fake2' => array('NameFemale', 'Surname' => null)
);

RavenDB Index created incorrectly

I have a document in RavenDB that looks looks like:
{
"ItemId": 1,
"Title": "Villa
}
With the following metadata:
Raven-Clr-Type: MyNamespace.Item, MyNamespace
Raven-Entity-Name: Doelkaarten
So I serialized with a type MyNamespace.Item, but gave it my own Raven-Entity-Name, so it get its own collection.
In my code I define an index:
public class DoelkaartenIndex : AbstractIndexCreationTask<Item>
{
public DoelkaartenIndex()
{
// MetadataFor(doc)["Raven-Entity-Name"].ToString() == "Doelkaarten"
Map = items => from item in items
where MetadataFor(item)["Raven-Entity-Name"].ToString() == "Doelkaarten"
select new {Id = item.ItemId, Name = item.Title};
}
}
In the Index it is translated in the "Maps" field to:
docs.Items
.Where(item => item["#metadata"]["Raven-Entity-Name"].ToString() == "Doelkaarten")
.Select(item => new {Id = item.ItemId, Name = item.Title})
A query on the index never gives results.
If the Maps field is manually changed to the code below it works...
from doc in docs
where doc["#metadata"]["Raven-Entity-Name"] == "Doelkaarten"
select new { Id = doc.ItemId, Name=doc.Title };
How is it possible to define in code the index that gives the required result?
RavenDB used: RavenHQ, Build #961
UPDATE:
What I'm doing is the following: I want to use SharePoint as a CMS, and use RavenDB as a ready-only replication of the SharePoint list data. I created a tool to sync from SharePoint lists to RavenDB. I have a generic type Item that I create from a SharePoint list item and that I serialize into RavenDB. So all my docs are of type Item. But they come from different lists with different properties, so I want to be able to differentiate. You propose to differentiate on an additional property, this would perfectly work. But then I will see all list items from all lists in one big Items collection... What would you think to be the best approach to this problem? Or just live with it? I want to use the indexes to create projections from all data in an Item to the actual data that I need.
You can't easily change the name of a collection this way. The server-side will use the Raven-Entity-Name metadata, but the client side will determine the collection name via the conventions registered with the document store. The default convention being to use the type name of the entity.
You can provide your own custom convention by assigning a new function to DocumentStore.Conventions.FindTypeTagName - but it would probably be cumbersome to do that for every entity. You could create a custom attribute to apply to your entities and then write the function to look for and understand that attribute.
Really the simplest way is just to call your entity Doelkaarten instead of Item.
Regarding why the change in indexing works - it's not because of the switch in linq syntax. It's because you said from doc in docs instead of from doc in docs.Items. You probably could have done from doc in docs.Doelkaartens instead of using the where clause. They are equivalent. See this page in the docs for further examples.

Arbitrarily nesting some attributes in rabl

I'm designing a new API for my project, and I want to return objects that have nested children as json. For that purpose i've decided to use RABL.
I want the client side to be able to understand whether the object is valid, and if not which fields are missing in order to save it correctly.
The design I thought of should include some fields as optional, under an optional hash, and the rest are required. The required fields should appear right under the root of the json.
So the output I try to describe should look something like this:
{
"name": "John",
"last_name": "Doe",
"optional": {
"address": "Beverly Hills 90210",
"phones":[{"number":"123456","name":"work"}, {"number":"654321","name":"mobile"}]
}
}
The above output example describes the required fields name and last name, and the not required address and phones (which is associated in a belongs_to-has_many relationship to the object). name, last_name and address are User's DB fields.
Playing with RABL I didn't manage so far to create this kind of structure.
Any suggestions? I'm looking for a DRY way to implement this for all my models.
RABL is really good in creating JSON structures on the fly, so I don't see why you couldn't achieve your goal. Did you try testing if a field is set to null-able in the schema, and thus presenting it as optional? It seems a good approach for me. For the nested children, just do the same, but extend the template for the children.
For example, in your father/show.rabl display a custom node :optional with all the properties that can be null.
Then, create a child/show.rabl with the same logic. Finally, go back to father/show.rabl and add a child node, extending the child/show.rabl template. This way you could achieve unlimited levels of "optionals".
Hope it helped you.
In this case I'd use the free form option.
From https://github.com/nesquena/rabl
There can also be odd cases where the root-level of the response
doesn't map directly to any object.
In those cases, object can be assigned to 'false'
and nodes can be constructed free-form.
object false
node(:some_count) { |m| #user.posts.count }
child(#user) { attribute :name }

Extract Drupal encoded data using MySQL from Ubercart table

I am writing an integration piece between Drupal/Ubercart and a in-house admin system.
The customer uses Ubercart products with attributes (e.g. a Plaque, which can contain a name, a company, a registration date and month). When an order is placed, the values entered for the attributes are written to uc_order_products, where the data field contains the actual values entered by the user. As far as I can tell, this is the only place where product attributes values, as entered by an end user to place an order, is stored.
The encoded attribute values uses a format seen all over Drupal tables to encode objects:
a:3:{s:10:"attributes";a:4:{s:26:"Name (to appear on plaque)";a:1:{i:0;s:10:"Some
Namee";}s:7:"Company";a:1:{i:0;s:28:"Some Company Name Goes here_";}s:19:"Certification
Month";a:1:{i:0;s:0:"";}s:18:"Certification Year";a:1:
{i:0;s:4:"2011";}}s:9:"shippable";s:1:"1";s:6:"module";s:10:"uc_product";}
And expanded, it looks like this:
a:3:
{
s:10:"attributes";
a:4:
{
s:26:"Name (to appear on plaque)";
a:1:
{
i:0;
s:10:"Some Namee";
}
s:7:"Company";
a:1:
{
i:0;
s:28:"Some Company Name Goes Herep";
}
s:19:"Certification Month";
a:1:
{
i:0;
s:0:"";
}
s:18:"Certification Year";
a:1:
{
i:0;
s:4:"2011";
}
}
s:9:"shippable";
s:1:"1";
s:6:"module";
s:10:"uc_product";
}
I there a simple way to get to the individual field values within this text using SQL? I can write a SQL function to go look for specifics, but I'd like to know if someone knows of an existing MySQL approach (perhaps within Drupal) which would do this.
Thanks!
That's a serialized array, meaning data that was processed by PHP's serialize() function before Drupal inserted it into the database. Use the opposite function unserialize() to turn that string back into an array.
Don't know if any built-in solution exists, but I ended up writing a SQL Function which takes as parameter text such as Name (to appear on plaque). The function then locates the text and extracts the succeeding { ... } block and from it retrieves the corresponding string value. Rough, but works in this case.
If someone has a better solution, I'd like to hear about it!