Taxonomy index: when joining nodes check if node revisions needs to be considered?

This was a question asked on an earlier issue regarding porting to D7. It needs a discussion here to resolve it.

In Drupal7 the taxonomy_index table links terms (tid) to nodes (nid). In Drupal6 the equivalent table was term_node and this contained the node id (nid), version id (vid) and term id (tid).

It appears that because the revision id is not in taxonomy_index then this data must refer to the latest node revision only, and is maintained to match the the nodes current terms. Now that taxonomies are added as fields the field_revision_* tables store the history of term changes if they were needed.

The taxonomy_index table is used three times in weblinks.module: weblinks_term_node_count(), weblinks_node_load() and _weblinks_get_query(), each time linking to the node table. It is also used once in weblinks.user.inc weblinks_user_form(), where it only links to taxonomy_term_data.

Comments

gstegemann’s picture

It appears that because the revision id is not in taxonomy_index then this data must refer to the latest node revision only

I would say yes. In D7 there is no way to map a revision of a node to a term.

One more thing needs to be considered: unpublished nodes are not kept in the taxonomy_index table. Therefore when Web Links Checker unpublishes a node it has to be removed from this table as well, and vice versa.

And third: the taxonomy_index table can be disabled on a site through variable 'taxonomy_maintain_index_table'. In other words when the variable 'taxonomy_maintain_index_table' exists and is set to FALSE we cannot rely on this table to retrieve node to term information.

jonathan1055’s picture

Thanks for this background info.

unpublished nodes are not kept in the taxonomy_index table. Therefore when Web Links Checker unpublishes a node it has to be removed from this table as well, and vice versa.

Hopefully we do not have to do this manually - if we use node_save() for the updating as discussed in #2415693: Weblinks Checker should update node_revision table then Taxonomy module should take care of this. We can check this after implementing node_save()

the taxonomy_index table can be disabled on a site through variable 'taxonomy_maintain_index_table'.

If if the variable is set to FALSE are you saying we should not use the table? I guess, then, that this requires each of the four uses of the table to be examined to see what alternatives there are for this scenario. I thought this issue was going to be simple and quick to close ...

jonathan1055’s picture

The variable 'taxonomy_maintain_index_table' is only used in taxonomy.module and field.api.php. Reading the comments in the header for taxonomy_field_presave() the variable defaults to TRUE and there is no admin config for this, it can only be set to FALSE by a custom module or maybe some contrib modules which would be fairly niche and specific. If this is the case then the site builder would know what they are doing, and they have a site which is not common. With this in mind, looking at our usages of the table:

  1. In weblinks_term_node_count() we can return 0 for the term count.
  2. Within our hook_node_load weblinks_node_load() there would be another way to get the terms, but that would be a separate issue and not a 7.x blocker
  3. In _weblinks_get_query() likewise we would have to get the node term data from the other taxonomy tables. This is more work and may not hardly be needed, so again it could be covered by that separate issue.
  4. For weblinks_user_form() if the taxonomy_index table was not maintained the processing could follow the same route as when taxonomy module does not exist, and all the users weblinks would be unclassified.

Hence for Weblinks 7.x-1.0 we need to return sensible results when the taxonomy_index table is not being maintained, but we can leave the implementation of alternative methods of term retrieval until after our 1.0 release, and then only when requested by our users - there may be not a single site which is both using Weblinks and has taxonomy_index table disabled.

gstegemann’s picture

We can check this after implementing node_save()

Yes. See also #962664: Taxonomy Index for unpublished entities.

The variable 'taxonomy_maintain_index_table' is only used in taxonomy.module

I agree with your implementation proposals.

there may be not a single site which is both using Weblinks and has taxonomy_index table disabled

Sure. Unless someone is running a site with a specific field storage engine.

jonathan1055’s picture

Title: Taxonomy index: when joining to nodes check if node revisions need to be considered? » Taxonomy_index table - check usage
Status: Active » Needs review
StatusFileSize
new2.26 KB
new2.14 KB
new3.18 KB
new3.07 KB

As expected, now that we are using node_save() when Weblinks Checker unpublishes a bad link the taxonomy_index table is automatically maintained (ie the rows are removed). When the url is good again and Checker republishes the node, the rows are added back into the taxonomy_index table. So that's good.

The four places where changes are required to check if we can use the the taxonomy_index table are:

  1. In weblinks_term_node_count() we can use table field_data_taxonomy_weblinks as an alternative source of term to node data.
  2. Function weblinks_node_load() is inefficient, and I don't know why we do it this way. It selects all fields from the node table for all nids being loaded. Then for each of them, checks taxonomy_index to get terms but joining back to node to filter on type 'weblinks'. We can use the existing ->taxonomy_weblinks property. This not only saves two database queries and avoids using taxonomy_index but means we still load the terms even when the node is unpublished.
  3. In _weblinks_get_query() we can use table field_data_taxonomy_weblinks if taxonomy_index is not being maintained
  4. In weblinks_user_form() likewise we can use table field_data_taxonomy_weblinks

Then this got me thinking ... If we can use table field_data_taxonomy_weblinks when taxonomy_index is not to be trusted, why can't we use it all the time? This would save the added complexity of checking, and avoid duplicating some code.

I have addresses each of the issues above, and made four separate patches - named accordingly. This is so you can test each change separately if you wish, or you can apply all four together as they do not clash. Once you've had time to look at the code, try it and have some thoughts then I will make a proper clean patch to test.

Jonathan

gstegemann’s picture

Function weblinks_node_load() is inefficient, and I don't know why we do it this way

See my answers below:

+++ b/weblinks.module
@@ -582,19 +603,53 @@ function weblinks_node_revision_delete($node) {
+  // ### Why do we do it this way? We have ->taxonomy_weblinks already existing in the $node objects.
+  // ### What is $node->taxonomy used for anyway? It sounds like it is a standard taxonomy field which we are overwriting?

Very simple. I didn't know better at the time when I started to port Web Links to D7. And in the early stages of the work the taxonomy field did not exist.

The property $node->taxonomy is used in the D6 version to assign a term to a Web Links node. There was no other way to accomplish this. In an earlier issue we discussed already to remove the property $node->taxonomy at a later time since it is basically obsolete now. To be consistent taxonomy information should be now taken from ->taxonomy_weblinks or table field_data_taxonomy_weblinks as you already proposed.

I will review and test your patches.

jonathan1055’s picture

Thanks for the background. I hope you realise that when I make these questions there is no implied criticism or anything like that. I just ask the questions, and you always have good answers. :-)

gstegemann’s picture

I hope you realise ...

Yes. Your questions are OK.

jonathan1055’s picture

I had a search but could not find the issue you mention, but anyway from what you say, that is good we can drop ->taxonomy. It will also be useful to make it consistent by using field_data_taxonomy_weblinks in all cases.

Would you like me to make a single clean patch based on this? The single debug patches are still useful if you want to see what I did and how the code developed.

gstegemann’s picture

Would you like me to make a single clean patch based on this?

Yes. Thanks.

jonathan1055’s picture

StatusFileSize
new5.71 KB

Here is a clean patch for 1, 3 and 4.
For item 2 I have created a separate issue #2449127: Remove $node->taxonomy and replace all usages with $node->taxonomy_weblinks

gstegemann’s picture

Many thanks. Good idea to create a separate issue for item 2.

My first comments:

+++ b/weblinks.user.inc
@@ -47,26 +47,27 @@ function weblinks_user_form($form, &$form_state, $account) {
-    $status[] = $node->status ? t('published') : t('not published');
+    $status[] = $node->status ? t('Published') : t('Not published');
     if ($node->promote) {
-      $status[] = t('promoted');
+      $status[] = t('Promoted');
     }
-    if ($node->sticky > 0) {  // >0 allows for sticky-encoded weighting.
-      $status[] = t('sticky');
+    if ($node->sticky == 1) {
+      $status[] = t('Sticky');
     }
-    if (!empty($node->moderate)) {
-      $status[] = t('moderated');
+    if (!empty($node->moderate)) { // how does this field get here?
+      $status[] = t('Moderated');

Is that a good idea to change the status text strings? How about already existing translations?

The field $node->moderate may exists when a moderation contributed module is installed and enabled like Modr8.

Generally: shouldn't there be checks whether the field/table field_data_taxonomy_weblinks really exists? I.e. when no Web Links vocabulary is used?

jonathan1055’s picture

Thanks for the review

a. Yes, maybe you are right. Those strings are ok to remain without a capital letter.

b. So you are saying that just using $query = db_select('node', 'n'); other contrib module can add extra fields to the node table? I'll take a look at Modr8 as that sounds interesting.

c. Yes you are are right we do need to check. In particular when a minimal installation is used without Taxonomy module. I had that on my list to check but forgot to mention it. There may even be some php5 warnings to fix.

jonathan1055’s picture

StatusFileSize
new6.08 KB

Modr8 actually changes the core node table by adding a 'moderate' field. I didn't know that was allowed, or at least I thought it was against good practice. I don't see why we should give extra importance to this particular contrib module and display the ->moderate field when there could be any number of fields added to the node table which "might be useful". I propose we drop it, but I'm ready to be persuaded if you have a strong view on this.

With Taxonomy module not enabled the changes to weblinks.module and weblinks.user.inc are all OK, as they will only be called when we are doing group work, which in turn means that the Taxonomy and Field modules must be available. Before installing Web Links I could not find a way to disable the 'field' core module. Is it possible? It says that it is required by 'Drupal' and there also seems to be a reciprocal requirement between 'Field' and 'Field SQL storage' which means that neither can be disabled. Is this due to some deep config setting?

Here is a patch with these things tidied up.

gstegemann’s picture

Regarding the ->moderate field: I think we should keep the code for the ->moderate field. It is there since I can remember. And node moderation is something which might be used on some sites, e.g. Web Links nodes added by regular website users which will be reviewed by the sites administrator before become published to the public.

Regarding module checks: no, the 'field' core module cannot be disabled. It is definitely required by Drupal. So every thing seems to be taken care of.

gstegemann’s picture

I have tested the patch. It works for me.

But some comments needs to be corrected:

+++ b/weblinks.module
@@ -1551,19 +1550,17 @@ function _weblinks_get_query($tid = 0, $sort = 'title', $limit = 0) {
     if ($tid === 0) {
       // Get the unclassified links, those with no row in taxonomy_index.
-      $query->addJoin('LEFT', 'taxonomy_index', 'tn', 'tn.nid = n.nid');
-      $query->isNull('tn.nid');
+      $query->addJoin('LEFT', 'field_data_taxonomy_weblinks', 't', 't.entity_id = n.nid');
+      $query->isNull('t.entity_id');
     }
     else {
       // Get the rows in taxonomy_index matching on term id(s).
-      $query->addJoin('INNER', 'taxonomy_index', 'tn', 'tn.nid = n.nid');
-      $query->where('tn.tid IN (:tid)', array(':tid' => $tid));
+      $query->addJoin('INNER', 'field_data_taxonomy_weblinks', 't', 't.entity_id = n.nid');
+      $query->where('t.taxonomy_weblinks_tid IN (:tid)', array(':tid' => $tid));
     }

The table name 'taxonomy_index' should changed to 'field_data_taxonomy_weblinks'.

Have you thought about the ->moderate field?

jonathan1055’s picture

OK we'll continue to add the 'moderate' field, although the Modr8 module does not have a full 7.x version yet. They have 500 installs of 7.x-dev and 1,700 at 6.x I think there are more widely used moderation modules such as Workbench Moderation which has 19,000 installs at 7.x. What would be really nice is if we could implement a hook or alter function allowing other modules to add their own entries to this table. Then Modr8 could add text and/or links, or any custom module could include extras. We could also add links on behalf of other modules if they are installed.

One question on coding style and your preference: query->fields('n', array('nid', ...)) fails if it specifies a field which does not exist. So to include the optional 'moderate' field we either load all fields from the node table (which is against best practice as we only need six out of the fourteen fields, therefore doing more db work that necessary), or we do a conditional if module_exists('modr8') which adds complexity and an extra bit of processing. I think in this situation the difference in page serve time is going to be negligible, but I'd like to hear your view on this.

gstegemann’s picture

Regarding the ->moderate field: I just picked Modr8 as an example. There might be other moderation modules using the method to add fields. Yes, a hook or alter function would be a better approach to support extras for other modules.

Your question: I think it is acceptable to query all fields here. A conditional would be difficult to maintain as we do not know which modules provide which extra fields now and in the future.

nancydru’s picture

The "moderate" flag was standard in the node table before 7.x. Given that Core has removed it, I would not support it at all, unless you want to stick in some kind of hook or query tag to allow other modules to do whatever they need done. I don't recall why WL needs to pay any attention to it.

If you want to do a "query all fields", then it would be faster to do a db_query rather than db_select.

jonathan1055’s picture

Thanks for the background Nancy. I'd like to drop it too, now that it is not in Core. It was only a text value after all, not an active link for the user to click. We could look at making the table properly customisable at a later date, after out 7.x release.

Thanks for the tip about db_query in preference to db_select, that's a useful reminder (I think I had read it, but forgotten)

gstegemann’s picture

Just to remind to some history of Web Links moderation please see #2089139: Typos in $node->promoted and $node->moderated, #289437: filter/refine and #316087: Collecting weblinks.

As the moderation concept of Modr8 might be not as flexible as compared to other modules like Workbench Moderation (based on node revisions) I think now as well it is OK to remove the ->moderate field.

jonathan1055’s picture

StatusFileSize
new6.21 KB

Thanks Gerhard for finding those past issues - one was even started by me, I'd forgotten that.
I'm pleased we all agree. Here is an updated patch.

gstegemann’s picture

Status: Needs review » Reviewed & tested by the community

one was even started by me, I'd forgotten that.

Yes, already a long time ago.

OK. I've tested the patch. Works for me. Thanks.

  • jonathan1055 committed 403fcb1 on 7.x-1.x
    Issue #2417699 by jonathan1055: Replace Taxonomy_index table with...
jonathan1055’s picture

Title: Taxonomy_index table - check usage » Replace taxonomy_index with field_data_taxonomy_weblinks table
Status: Reviewed & tested by the community » Fixed

Thank you both for all your input and help.

  • jonathan1055 committed fd8b69f on 7.x-1.x
    Issue #2417699 by jonathan1055: changelog.txt for Replace taxonomy_index
    

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.