Fetching content order by multiple columns - mysql

Documentation: https://docs.bolt.cm/content-fetching#ordering-results
I'm trying to fetch a set of records and order them based on two fields. I have created the following request to do so:
{% setcontent records = 'artists' where { gender: 'Male' } orderby 'surname, title ASC' %}
This works fine when using bolt.db as the database but in MySQL it does not. I have the feeling that I have done something similar to this before so I'm wondering if it's version change related. This implementation is using v1.6.5.

OK I've got this working with a quick fix.
What I've done is modified the Storage.php file ever so slightly.
private function getEscapedSortorder($name, $prefix = 'r')
{
// HACK: multiple sort columns
if (strpos($name, ',') !== FALSE) {
return $name;
}
// END HACK: multiple sort columns
list ($name, $asc) = $this->getSortOrder($name);
...
In my contenttypes I can now define sort: surname, title which will automatically do the ASC order, or I could do sort: surname DESC, title ASC or whatever SQL will allow really.
This could easily be modified to handle -surname, -title, etc.

I've not tested this but the usual syntax for MySQL would be:
order surname ASC, title ASC
try tweaking the orderby string to be the same format and see if that does the job.

Related

Creating GORM dynamic query with optional paramters

I've been stuck on a GORM issue for about a full day now. I need to be able to filter a messages table on any of 4 things: sender, recipient, keyword, and date range. It also has to paginate. Filtering by sender and recipient is working, and so is pagination. So far this is the query that I have come up with, but it does not seem to work for date ranges or keywords.
Here is how I am selecting from MySQL
db.Preload("Thread").Where(query).Scopes(Paginate(r)).Find(&threadMessages)
I am creating the query like this:
var query map[string]interface{}
Then based on which parameters I am passed, I update the query like this by adding new key values to the map:
query = map[string]interface{}{"user_id": sender, "recipient_id": recipient}
For dates it does not seem to work if I try something like this:
query = map[string]interface{}{"created_at > ?": fromDate}
And for a LIKE condition is also does not seem to work:
query = map[string]interface{}{"contents LIKE ?": keyword}
The reason I chose this approach is that I could not seem to get optional inputs to work in .Where since it takes a string with positional parameters and null positional parameters seem to cause MySQL to return an empty array. Has anyone else dealt with a complicated GORM issue like this? Any help is appreciated at this point.
Passing the map[string]interface{} into Where() only appears to work for Equals operations, or IN operations (if a slice is provided as the value instead).
One way to achieve what you want, is to construct a slice of clause.Expression, and append clauses to the slice when you need to. Then, you can simply pass in all of the clauses (using the ... operator to pass in the whole slice) into db.Clauses().
clauses := make([]clause.Expression, 0)
if mustFilterCreatedAt {
clauses = append(clauses, clause.Gt{Column: "created_at", fromDate})
}
if mustFilterContents {
clauses = append(clauses, clause.Like{Column: "contents", Value: keyword})
}
db.Preload("Thread").Clauses(clauses...).Scopes(Paginate(r)).Find(&threadMessages)
Note: If you're trying to search for content that contains keyword, then you should concatenate the wildcard % onto the ends of keyword, otherwise LIKE behaves essentially the same as =:
clause.Like{Column: "contents", Value: "%" + keyword + "%"}
My final solution to this was to create dynamic Where clauses based on which query params were sent from the client like this:
fields := []string{""}
values := []interface{}{}
If, for example, there is a keyword param:
fields = []string{"thread_messages.contents LIKE ?"}
values = []interface{}{"%" + keyword + "%"}
And to use the dynamic clauses in the below query:
db.Preload("Thread", "agency_id = ?", agencyID).Preload("Thread.ThreadUsers", "agency_id = ?", agencyID).Joins("JOIN threads on thread_messages.thread_id = threads.id").Where("threads.agency_id = ?", agencyID).Where(strings.Join(fields, " AND "), values...).Scopes(PaginateMessages(r)).Find(&threadMessages)

sequelize fulltext query using parameters

Hi I'm currently trying to query records from db and these are the conditions
I receive 'order by', 'order (desc/asc)', 'limit', 'offset' from the frontend
I also need to search the record using match...against. 'like' is too slow for searching.
There's a mapped model with this query.
so I tried
let order_by = req.query.orderby;
let order = req.query.order;
let page = req.query.pagenum;
let perpage = req.query.parpage;
let searchword = req.query.foodsearch;
let offset = (parseInt(page) - 1) * parpage;
let foods = await models.food.findAll({
limit: parseInt(perpage),
offset: offset,
order: [
[order_by, order]
],
// where: Sequelize.literal
// (
// `MATCH
// (Name, Place, RestoNum, Ingredient, ChefName, Region...)
// AGAINST
// ( ? IN NATURAL LANGUAGE MODE)`,
// { replacements: [ searchword ] }
// )
});
but the commented part seems wrong in this code.
I tried the raw query, but then I can't parameterize those order by, order, offset, limit variables.
I don't want to just add them like ${orderby} because it's risky.
Please let me know if you have any solution for this issue.
Thank you in advance!
You're confusing the Sequelize.literal() and sequelizeInstance.query() APIs.
.literal() only take a string. If you want to use the object notation for your query, your commented code will work. Except that there is no second argument. You will need to concatenate-in or interpolate-in your search term into the AGAINST clause. Also, don't forget your quotes. The output of the literal() is essentially a string. Your MySQL FTS parameter will need the correct type of quotes around it, just as they would appear in your raw SQL query.
.query() DOES take an options parameter. Through this, you don't have to use string interpolation, you can use named replacements or bound-parameters. This will not only allow you to place in your searchword parameter, but whatever ORDER BY clause you want, as well.
I would go with Option 1. That's what we are doing for our FTS, in MS SQL.

MySql Query build using Kohana's ORM

I'm having a hard time to translate the following query into kohana's ORM.
So, if I do the following works fine:
$query = DB::query(Database::SELECT, 'SELECT id_book, MATCH(title, author, isbn) AGAINST (:str) AS score FROM tab_books WHERE status = 1 AND MATCH(title, author, isbn) AGAINST (:str) HAVING score > '.$score.' ORDER BY score DESC LIMIT 100');
However, I need to use an specific class model. So far I have:
$books = new Model_Book();
$books = $books->where('status', '=', 1);
$books = $books->where(DB::expr('MATCH(`title`,`author`,`isbn`)'), 'AGAINST', DB::expr("(:str)"))->param(':str', $search_terms);
Which works fine, except for the fact that I'm unable to use the score value. I need the score because since I changed the table engine to InnoDB, the 2nd query is returning a lot of results.
ORM here: https://github.com/kohana/orm/blob/3.3/master/classes/Kohana/ORM.php
Thank you for your time.
So, you don't use query builder, but ORM object finding.
In first case you take result array on second array of objects.
Trust me, you don't want use list objects. (It's extremely slow)
$sq = DB::expr('MATCH(title, author, isbn) AGAINST (:str) AS score')
->param(":str", $search_terms);
$wq = DB::expr('MATCH(title, author, isbn)');
$query = DB::select('id_book', $sq)
->from('tab_books') // OR ->from($this->_table_name) for model method
->where('status','=',1) ->where($wq, 'AGAINST ', $search_terms)
->order_by('score', desc)->limit(100) //->offset(0)
->having('score', '>', $score);
$result = $query->execute()->as_array();
for query test:
die($query->compile(Database::instance()));
OT: Use
$books = ORM::factory('Book')->full_text($search_terms, $score);
instead $books = new Model_Book();

Wordpress orderby second word of meta_value

I have a page template "By Author" (ID 1814) I am using to query my posts (Title: book titles & Content: descriptions) by meta_key: author_name, which equals both the book author's first & last name_ However, I would like to order these posts by just the author's last name — the second word in the meta_value. I am able to filter get these results in the loop by using:
$values = get_post_custom_values("author_name");
$alpha = end(explode(' ',$values[0]));
But I don't know how to get these posts to order by this result not by author_name's first name. I found the query below for the functions.php file, which I think puts me on the right path but returns a 404 error on my page when I use it.
Code from here: http://randyhoyt.com/wordpress/custom-post-type-ordering/
I haven't even added the SQL query to explode (or whatever it is in SQL not PHP). Not sure if that code isn't working because I am querying on a page template and not an archive.php file? Any suggestions to get me on the right path?
add_filter('posts_join', 'author_join' );
function author_join($wp_join) {
if(is_page(1814)) {
global $wpdb;
$wp_join .= " LEFT JOIN (
SELECT post_id, meta_value as author_name
FROM $wpdb->postmeta
WHERE meta_key = 'author_name' ) AS DD
ON $wpdb->posts.ID = DD.post_id ";
}
return ($wp_join);
}
add_filter('posts_orderby', 'author_order' );
function author_order( $orderby ) {
if(is_page(1814)) {
$orderby = " DD.post_title ASC";
}
return $orderby;
}
I've dealt with a similar issue where band names starting with "The ..." were alphabetically ordered at T. The fix I implemented was to reverse the order of the name in the database to satisfy the MySQL query, and then the display page can use a string function to reverse the name back to the proper order.
Something like this on the display page would fix the output:
// The author's name, reverse ordered.
$author_name = 'Doe,John';
// Break the full name into its parts.
list($last_name,$first_name) = explode(',',$author_name);
// Then display the name in the correct order.
echo(first_name.' '.last_name);

mySQL $REQUEST defaults

mySQL NOOB question:
Table has a column = AgeGroup. It is populated with the following data options: U40, 40-44, 45-49, 50-54, 55-59, 60+.
I have a form which allows the user to select the 'Gender' and 'AgeGroup' they wish to view. This then uses a "WHERE" clause of "SELECT" SQL query. It uses in the following format:
FROM
#__test1
WHERE
EventName = '2011EoSummer' AND
Gender = {$REQUEST:Gender};
if(isset($_REQUEST['Age'])) AND AgeGroup = {$_REQUEST['Age']}
In the form, there is a option to get all ages via "Overall" but there is no data called "Overall" in the AgeGroup column. Overall should default to ALL age groups, but I don't know how this would read in the SQL query.
Example 1 URL: /&Gender=Men&AgeGroup=U40 => would display data in 'U40' 'Men'
Example 2 URL: /&Gender=Men&Age=Overall => would display ALL Age data
If I'm reading this right, when you have "overall" selected you want to return all the age groups (so as not to limit by any age group)?
If so you need to remove the AgeGroup clause in your SQL statment.
<?PHP
$sql = "SELECT * FROM tbl WHERE Gender = {$_REQUEST['gender']}";
if(isset($_REQUEST['age']))
{
$sql.= " AND AgeGroup = {$_REQUEST['age']}"
}
?>
This will default to overall, so you may want to change the logic appropriately, hope I got what you were after, if not ignore it!
Also as a side note $_REQUEST isn't the best to use, if your using a form as your action collector set it to post and use $_POST instead, if you cant use $_GET to pull your data out of the url instead of request.
Edit: Added in brackets to make it easier to read.