(drupal)a difficulty code to understand,get the same article's title under the same term - module

if ($node->taxonomy) {
$query = 'SELECT DISTINCT(t.nid), n.nid, n.title FROM {node} n INNER JOIN {term_node} t ON n.nid = t.nid WHERE n.nid != %d AND (';
$args = array($node->nid);
$tids = array();
foreach ($node->taxonomy as $term) {
$tids[] = 't.tid = %d';
$args[] = $term->tid;
}
$query .= implode(' OR ', $tids) . ')';
$result = db_query_range($query, $args, 0, 10);
while ($o = db_fetch_object($result)) {
echo l($o->title, 'node/' . $o->nid);
}
}
the code is from a drupal guru. . used to get the article's title under the same term in node.tpl.php, i have researched it two days, although know some part of it. the principle of the code i still don't know. expect someone can explain more details about it for me .many thanks.

Short version:
It gets the array of tags of the node, retrieves the first 10 nodes that use at least one of these tags and outputs a link for each of these 10 results.
Detailed version:
First of all, the variable "$node" is an object that contains the data about a specific node (e.g. a Page or Story node).
For example, "$node->title" would be the title of that node.
"$node->taxonomy" tests is that node is tagged (because if it has no tags, it cannot retrieve the other nodes using the same tag(s).
When there is one or several tags associated with that node/page/story, $node->taxonomy is an array .
Now about the SQL query:
"node" is the database table that stores the base fields (non-CCK) of every node.
"term_node" is the database table that contains the combination of tag (which is called a "taxonomy term") and node.
In both tables, "nid" is the "unique Node ID" (which is an internal autoincremented number). Because this column is in both tables, this is how the tables are joined together.
In "term_node", "tid" is the "unique Term ID" (which is also an internal autoincremented number).
The "node" table is aliased "n", therefore "n.nid" means "the Node ID stored in table node".
The "term_node" table is aliased "t", therefore "t.tid" means "the Term ID stored in table term_node".
The "foreach" loop goes thru the array of tags to extract the TermID of each tag used by the node in order to add it in the SQL query, and implode converts to a string.
The loop stores a piece of SQL query for each tag in variable $tids and stores the actual value in variable $args because Drupal database calls are safer when the arguments are passed separately from the SQL query: "%d" means "integer number".
"db_query_range" is a function that selects multiple rows in the database: here, "0 10" means "retrieve the first 10 results".
"db_fetch_object" in the "while" loop retrieves each result and stores it in the variable "$o", which is an object.
Therefore "$o->title" contains the value of the column "title" retrieved by the SQL query.
The function "l" is the drupal functin that creates the code for an HTML link: the first argument is the name of the link, the second argument is the drupal path: in Drupal, any node can be accessed by default using "www.yoursite.com/node/NodeID",
which is why it gives the path "node/123" (where 123 is the "Node ID").
This function is useful because it transparently handles custom paths, so if your node has a custom path to access it using "www.yoursite.com/my-great-page" instead, it will create a link to that page instead of "www.yoursite.com/node/123" automatically.

I wouldn't exactly call the guy who wrote this a guru, you could do this a lot prettier. Anyways what he does it create a query that looks like this:
SELECT DISTINCT(t.nid), n.nid, n.title FROM {node} n
INNER JOIN {term_node} t ON n.nid = t.nid
WHERE n.nid != %d
AND (t.tid = %d OR t.tid = %d OR ... t.tid = %d);
The end result is that he selects all the node ids and titles (only once) that share at least one term with the selected node, but isn't the node itself.

Related

How to retrieve the list of dynamic nested keys of BigQuery nested records

My ELT tools imports my data in bigquery and generates/extends automatically the schema for dynamic nested keys (in the schema below, under properties)
It looks like this
How can I get the list of nested keys of a repeated record ? so for example I can group by properties when those items have said property non-null ?
I have tried
select column_name
from my_schema.INFORMATION_SCHEMA.COLUMNS
where
table_name = 'my_table
But it will only list first level keys
From the picture above, I want, as a first step, a SQL query that returns
message
user_id
seeker
liker_id
rateable_id
rateable_type
from_organization
likeable_type
company
existing_attempt
...
My real goal through, is to group/count my data based on a non-null value of a 2nd level nested properties properties.filters.[filter_type]
The schema may evolve when our application adds more filters, so this need to be dynamically generated, I can't just hard-code the list of nested keys.
Note: this is very similar to this question How to extract all the keys in a JSON object with BigQuery but in my case my data is already in a shcema and it's not a JSON object
EDIT:
Suppose I have a list of such records with nested properties, how do I write a SQL query that adds a field "enabled_filters" which aggregates, for each item, the list of properties for wihch said property is not null ?
Example input (properties.x are dynamic and not known by the programmer)
search_id
properties.filters.school
properties.filters.type
1
MIT
master
2
Princetown
null
3
null
master
Example output
search_id
enabled_filters
1
["school", "type"]
2
["school"]
3
["type"]
Have you looked at COLUMN_FIELD_PATHS? It should give you the paths for all columns.
select field_path from my_schema.INFORMATION_SCHEMA.COLUMN_FIELD_PATHS where table_name = '<table>'
[https://cloud.google.com/bigquery/docs/information-schema-column-field-paths]
The field properties is not nested by array only by structures. Then a UDF in JavaScript to parse thise field should work fast enough.
CREATE TEMP FUNCTION jsonObjectKeys(input STRING, shownull BOOL,fullname Bool)
RETURNS Array<String>
LANGUAGE js AS """
function test(input,old){
var out=[]
for(let x in input){
let te=input[x];
out=out.concat(te==null ? (shownull?[x+'==null']:[]) : typeof te=='object' ? test(te,old+x+'.') : [fullname ? old+x : x] );
}
return out;
Object.keys(JSON.parse(input));
}
return test(JSON.parse(input),"");
""";
with tbl as (select struct(1 as alpha,struct(2 as x, 3 as y,[1,2,3] as z ) as B) A from unnest(generate_array(1,10*1))
union all select struct(null,struct(null,1,[999])) )
select *,
TO_JSON_STRING (A ) as string_output,
jsonObjectKeys(TO_JSON_STRING (A),true,false) as output1,
jsonObjectKeys(TO_JSON_STRING (A),false,true) as output2,
concat('["', array_to_string(jsonObjectKeys(TO_JSON_STRING (A),false,true),'","' ) ,'"]') as output_sring,
jsonObjectKeys(TO_JSON_STRING (A.B),false,true) as outpu
from tbl

request where there are at least 2 records in other table

I have this doc xml(short version) :
<Artiste>
<artiste a_id="A62" a_p_id="UK" a_date_nais="07/06/1952" a_sexe="M">
<a_prenom>Liam</a_prenom>
<a_nom>Neeson</a_nom>
</artiste>
<artiste a_id="A66" a_p_id="UK" a_date_nais="08/09/1971" a_sexe="M">
<a_prenom>Martin</a_prenom>
<a_nom>Freeman</a_nom>
</artiste>
<Film>
<film f_id="F1" f_p_id="FR" f_r_id="A61">
<f_genre>P</f_genre>
<f_titre>Banlieue 13</f_titre>
<f_date_sortie>10/11/2004</f_date_sortie>
<f_resume>fiction française</f_resume>
<f_role ro_nom="Leïto" ro_a_id="A63"/>
<f_role ro_nom="Lola" ro_a_id="A64"/>
</film>
<film f_id="F2" f_p_id="NZ" f_r_id="A59">
<f_genre>A</f_genre>
<f_titre>Les seigneurs des anneaux</f_titre>
<f_date_sortie>19/12/2005</f_date_sortie>
<f_resume>fiction américaine</f_resume>
<f_role ro_nom="Pêcheur" ro_a_id="A25"/>
<f_role ro_nom="Sirène" ro_a_id="A2"/>
</film>
</Film>
An artist play in a film(movie), an artist has a 'a_id" field in Artiste which is the same then in ro_a_id in Film
I want to select the name and first name (a_prenom, a_nom) of every artists that have played in at least 2 movies (film)
This is what I've done :
for $artiste in doc('S:/path/file.xml')//Artiste/artiste
(: retrieve film $artiste is working in :)
let $film := ('S:/path/file.xml')//Film/film[#ro_a_id=$artiste/#a_id]
where count(#ro_a_id)>=2
order by $artiste/#a_id
return $x/a_nom, $x/a_prenom
So I don't know how to join and make the request, and I also don't know how to return 2 fields (I know that $x/a_nom, $x/a_prenom line generates an error)
You're very close, but your query has a couple of problems:
The return clause references an undefined variable $x. This should be changed to $artiste
Once that is fixed, you can return each actor's two name elements by constructing a sequence—by wrapping the items in parentheses: ($artiste/a_nom, $artiste/a_prenom). Alternatively you could return a single item, e.g., a string created by concatenating the two name parts, with concat($artiste/a_nom, " ", $artiste/a_prenom).
Your where clause should reference the $film variable—specifically, $film/f_role/#ro_a_id.
Your sample data here doesn't contain any artists who appear in more than two of the films listed. So the where clause, even if fixed, will result in 0 hits.
I've posted a revised query to http://xqueryfiddle.liberty-development.net/nbUY4kp/1 showing these suggested changes. You'll see that I commented out the where clause so that we get some results.
The easiest way to make your query work is as follows:
for $artiste in doc('S:/path/file.xml')//Artiste/artiste
(: retrieve film $artiste is working in :)
let $film := doc('S:/path/file.xml')//Film/film[f_role/#ro_a_id=$artiste/#a_id]
where count($film)>=2
order by $artiste/#a_id
return ($artiste/a_nom, $artiste/a_prenom)
Here are the things I changed:
The expression ('S:/path/file.xml') in line 3 should probably be doc('S:/path/file.xml').
#ro_a_id is an attribute of f_role, not film.
You have to count the film elements, #ro_a_id is not in scope on line 4.
$x is never declared, you probably mean $artiste.
The final problem in the last row is that the FLWOR expression ends after the comma, so $artiste/a_prenom is not part of it. You can solve that by surrounding both parts with parentheses.

How to query with string variable in rails 3.2.12?

We would like to store both rails query string and table name in db and retrieve them for execution at run time. Here is the scenario:
Retrieve active customer records from customers table. Let's say we have 2 variable defined as:
table_name = 'Customer'
query_string = ':active => true'
In rails, the query could be:
records = Customer.where(:active => true)
Now with table name and query string stored in variables table_name and query_string, is it possible to assemble a query string with 2 variables like:
records = table_name.where(query_string) ?
Thanks for the help.
You could do this, but it's not generally recommended to evaluate a string as a hash. Also, table_name is an unfortunate name for the variable, because you actually are storing the class name (table would be 'customers'). In any event, what you are missing is the eval of these strings:
records = class_name.constantize.where(instance_eval(query_string))
Note that running instance_eval on a user-inputted string can be disasterous for security and the well-being of your application. Use with care, and stick to building an actual hash.
The definition of instance_eval is: Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj).
Another way to eval a query string is to include where in the string, like:
table_name = 'Customer'
query_string = 'where(:active => true)'
Then the record could be retrieved by:
records = table_name.constantize.instance_eval(query_string)
By putting where into the string, we can use the full power of instance_eval instead of just returning the source code to where as in the question above.

Active Record Query where value in array field

I have a table called " Stat " in my MongoDB database in Rails 3 .
In that table, there is an array field called "services" .
I want to find all Stats that have a services array that contains the value "lights" .
I want to do something like this :
#stats = Stat.all
#stats1 = #stats.where("services contains lights")
Rails.logger.info "result: #{#stats1.count} "
I've tried various things and Googled it extensively, found some leads but nothing that seems to work. I have four records that should match this query but the above returns a zero set.
Is what I want to do possible in rails 3 / mongo ?
Try this,
#stats = Stat.all
#stats1 = #stats.where("'lights' = ANY (services)")
Ok I found the answer to this question:
#stats = #stats.where(:services.in =>['lights'] )
and I also found by poking around that the inverse is:
#stats = #stats.where(:services.nin =>['lights'] )
nin instead of in

Merging result from 2 columns with same name and not over-writing one

I have a simple MySQL query like:
SELECT *
FROM `content_category` CC , `content_item` CI
WHERE CI.content_id = '" . (int)$contentId . "'
AND CI.category_id = CC.category_id
AND CI.active = 1
Both tables have a column called configuration one of which gets overwritten in the query i.e only content_item.configuration is returned in the result.
Short of implicitly naming and aliasing the columns like
SELECT CC.configuration as `category_configuration`,
CC.category_id as `.....
is there a way of selecting ALL data i.e * from both and resolve those duplicate column names in a non-destructive way.
You don't need to alias ALL the columns, just the one conflicting one:
SELECT *,CC.configuration as cc_conf, CI.configuration as ci_conf FROM `content_category` CC , `content_item` CI WHERE
CI.content_id = '" . (int)$contentId . "'
AND CI.category_id = CC.category_id
AND CI.active = 1
This demonstrates one of the many reasons why using the * wildcard is not a good practice all the time. All the columns are returned in the result set, but if you access them via an associative array or via object properites in your host language (e.g. PHP or Ruby) you can naturally only have one of the columns associated with each key or object property.
Solutions:
Fetch them all and reference the columns by ordinal position.
Stop using the wildcard for one table or the other, and give column aliases.
Rename your columns to be distinct.
Define a VIEW with the column aliasing spelled out, and query from the view.