I want to find posts which have a specific post meta key/value or posts which are in a specific category. Here is the query. I want to combine the tax_query and the meta_query with OR, but AFAICS there's no way to do this.
$args = [
'post_type' => 'my-event',
'post_status' => 'publish',
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
'cat' => 'home',
'tax_query' => [
[
'taxonomy' => 'event-cat',
'terms' => [
'open',
],
'field' => 'slug',
'operator' => 'IN',
'include_children' => true,
],
],
'meta_query' => [
[
'key' => 'event_author',
'value' => [1,7,11,15],
'compare' => 'IN',
],
],
];
$loop = new WP_Query($args);
Result should be:
All posts (events) which are in the category 'open' (no matter who the author is) AND all posts which are from one of the specified authors (no matter in which category the event is).
On SO I found a few similar questions and I think I have to create a SQL query to find a solution but I don't know how to do it. Hope that someone can help me here.
Thanks and best regards.
In order to make custom,unconventional and a bit complicated queries like this, you have to learn some basic SQL Syntax. Trust me, SQL basics are not so hard to learn, and they are really worth it if you consider how many projects in many different programming languages need some SQL knowledge.
WordPress gives you the option to make custom queries with wpdb::get_results() function. In case you already know how to use SQL syntax, try exploring the wordpress database a little, and you will find what table columns you need to extract to get the result you want.
Related
I have a comparative set of arguments for WP_Query involving a custom field.
On a page I need to say "Are there going to be results?, if so display a link to another page that displays these results, if not ignore" There are between 500 and 1200 posts of this type but could be more in the future. Is there a more efficient or direct way of returning a yes/no to this query?
$args = array(
'post_type' => 'product',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'partner',
'value' => $partner,
'compare' => 'LIKE',
),
),
);
$partner_query = new WP_Query($args);
if ($partner_query->have_posts() ) { [MAKE LINK] }
The link is not made from data returned, we already have that information.
Perhaps directly in the database. My SQL is not up to phrasing the query which in English is SELECT * from wp_posts WHERE post_type = 'product'} AND (JOIN??) post_meta meta_key =
partner AND post_id = a post_id that matches the first part of the query.
And if I did this, would this be more efficient that the WP_Query method?
Use 'posts_per_page' => 1 and add 'no_found_rows' => true and 'fields' => 'ids'. This will return the ID of a matching post, and at the same time avoid the overhead of counting all the matching posts and fetching the entire post contents. Getting just one matching post id is far less work than counting the matching posts. And it's all you need.
Like this:
$args = array(
'post_type' => 'product',
'posts_per_page' => 1,
'no_found_rows' => true,
'fields' => 'ids',
'meta_query' => array(
array(
'key' => 'partner',
'value' => $partner,
'compare' => 'LIKE',
),
),
);
$partner_query = new WP_Query($args);
if ($partner_query->have_posts() ) { [MAKE LINK] }
no_found_rows means "don't count the found rows", not "don't return any found rows". It's only in the code, not the documentation. Sigh.
I have a null-able relation model, and I am using this standard code to retrieve my records:
$this->paginate = [
'contain' => ['Clients']
];
$coupons = $this->paginate($this->Coupons);
I am getting just the records that have client associated.
what is the best practice to make the contain work like OR, and not AND
EDIT: the relationship is seted as the following:
$this->belongsTo('Clients', [
'foreignKey' => 'client_id',
'joinType' => 'INNER'
]);
I have solved it by setting the joinType to LEFT:
$this->belongsTo('Clients', [
'foreignKey' => 'client_id',
'joinType' => 'LEFT'
]);
I have this silverstripe query that does not work ( it outputs all messages and not the ones with the date range )
What would be the best way to tackle this query?
Im fairly new to silverstripe and havent been able to find information on how to print the raw query.
return = Message::get()
->filter(array(
'IsPublished' => true,
'StartPublication:LessThanOrEqual' => date('Y-m-d'),
'Priority' => array('High', 'Normal')
))
->where("\"StopPublication\" >= ".date('Y-m-d')." OR \"StopPublication\" IS NULL")
->sort('StartPublication', 'DESC')->limit($this->getLimit());
The correct answer is to not use where() - this is a trap method that a lot of learners fall into (presumably due to the name). It's intended basically only for very complex things that the ORM just can't handle.
You're calling filter at least, which is the correct thing. But what you want instead of where() is filterAny():
Message::get()
->filter([
'IsPublished' => true,
'StartPublication:LessThanOrEqual' => 'now',
'Priority' => ['High', 'Normal']
])
->filterAny([
'StopPublication:GreaterThanOrEqual' => 'now',
'StopPublication' => null
])
->sort('StartPublication', 'DESC')
->limit($this->getLimit());
As the other answer already specifies, do not use an = on the return (or put a $ in front of return to make it a variable), and to return the query itself use $datalist->sql() http://api.silverstripe.org/3.1/class-DataList.html#_sql
But - seeing the docs on SQLQuery is wrong, because you're not using SQLQuery. You're using the ORM, so this doc page is far more relevant: http://docs.silverstripe.org/en/3.1/developer_guides/model/data_model_and_orm/#filterany
For starts return = Message::get() its just return Message::get()
I assume that you have set php error reporting so that it outputs errors and SS is also in development mode so it won't hide error outputs.
The answer to your question is to to do either:
to output it to the output html:
Debug::dump(Message::get()
->filter(array(
'IsPublished' => true,
'StartPublication:LessThanOrEqual' => date('Y-m-d'),
'Priority' => array('High', 'Normal')
))
->where("\"StopPublication\" >= ".date('Y-m-d')." OR \"StopPublication\" IS NULL")
->sort('StartPublication', 'DESC')->limit($this->getLimit())->sql());
or output it to the project roots log file
Debug::log(Message::get()
->filter(array(
'IsPublished' => true,
'StartPublication:LessThanOrEqual' => date('Y-m-d'),
'Priority' => array('High', 'Normal')
))
->where("\"StopPublication\" >= ".date('Y-m-d')." OR \"StopPublication\" IS NULL")
->sort('StartPublication', 'DESC')->limit($this->getLimit())->sql());
See http://docs.silverstripe.org/en/developer_guides/model/sql_query/
In SQL, I can search email addresses pretty well with SQL LIKE.
With an email "stack#domain.com", searching "stack", "#domain.com", "domain.com", or "domain" would get me back the desired email address.
How can I get the same result with ElasticSearch?
I played with nGram, edgeNGram, uax_url_email, etc and the search results have been pretty bad. Please correct me if I'm wrong, it sounds like I have to do the following:
for index_analyzer
use "keyword", "whitespace", or "uax_url_email" tokenizer so the email don't get tokenized
but wildcard queries don't seem to work (with tire at least)
use "nGram" or "edgeNGram" for filter
I always get way too many unwanted results like getting "first#domain.com" when searching "first-second".
for search_analyzer
don't do nGram
One experiment code
tire.settings :number_of_shards => 1,
:number_of_replicas => 1,
:analysis => {
:filter => {
:db_ngram => {
"type" => "nGram",
"max_gram" => 255,
"min_gram" => 3 }
},
:analyzer => {
:string_analyzer => {
"tokenizer" => "standard",
"filter" => ["standard", "lowercase", "asciifolding", "db_ngram"],
"type" => "custom" },
:index_name_analyzer => {
"tokenizer" => "standard",
"filter" => ["standard", "lowercase", "asciifolding"],
"type" => "custom" },
:search_name_analyzer => {
"tokenizer" => "whitespace",
"filter" => ["lowercase", "db_ngram"],
"type" => "custom" },
:index_email_analyzer => {
"tokenizer" => "whitespace",
"filter" => ["lowercase"],
"type" => "custom" }
}
} do
mapping do
indexes :id, :index => :not_analyzed
indexes :name, :index_analyzer => 'index_name_analyzer', :search_analyzer => 'search_name_analyzer'
indexes :email, :index_analyzer => 'index_email_analyzer', :search_analyzer => 'search_email_analyzer'
end
end
Specific cases that don't work well:
emails with hyphen (eg. email-hyphen#domain.com)
query string '#' at the beginning or end
exact matches
searching with wildcard like '#' gets very unexpected results.
Suppose I have, "aaa#email.com", "aaa_0#email.com", and "aaa-0#email.com, searching "aaa" gives me "aaa#a.com" "aaa-0#email.com. Searching "aaa*" give me everything, but "aaa-*" gives me nothing. So, how should I do exact match wildcard queries? For these type of queries, I get pretty much the same results for different tokenizer/analyzer.
I do these after each mapping change:
Model.tire.index.delete
Model.tire.create_elasticsearch_index
Model.tire.index.import Model.all
References:
Configure ElasticSearch to use ngram by default. - SQL LIKE %% behavior
http://euphonious-intuition.com/2012/08/more-complicated-mapping-in-elasticsearch/
Considering what you are trying to accomplish, KeywordAnalyzer might be a reasonable choice of analyzer, though I don't see anything that would cause problems with a WhitespaceAnalyzer.
I suspect you are running into problems with the query parsing and analysis, although you haven't really described how you are querying. Simplest case would be to simply use term or prefix queries.
It does seem a bit like StandardAnalyzer would serve your purpose here, mostly (differentiating between "aaa_0" and "aaa-0" would be a problem), as long as it is applied consistently, and your query is correct.
Explanation for the use of $condition and $param in findByAttributes in Yii
In most case, this is how I use findByAttributes
Person::model()->findByAttributes(array('first_name'=>$firstName,'last_name'=>$lastName));
Copy from this thread http://www.yiiframework.com/forum/index.php/topic/21178-findbyattributes-example/
Explain what you want to do and where is your errors.
Try to read documentation http://www.yiiframework.com/doc/api/1.1/CActiveRecord#findByAttributes-detail
When you do a find in Yii's CActiveRecord using the attributes of the model, you would most likely find by some equality.
Person::model()->findByAttributes(array(
'first_name' => $firstName,
'last_name' => $lastName,
));
In English, this translates to "Find me a person whose last name is $lastname and whose first name is $firstname". But this is a bad example. Consider the following.
Person::model()->findByAttributes(array(
'age' => 18,
));
This is a better example which translates, in English, to "Fetch me all eighteen year olds" (that is, "where age = 18). This can be re-written with conditions like so
Person::model()->findByAttributes(array(
'condition' => 'age = :age',
'params' => array(
':age' => 18,
),
));
So far, there may seem to be little benefits. But such expressions make us able to use more boolean operators. For example...
Person::model()->findByAttributes(array(
'condition' => 'age < :age',
'params' => array(
':age' => 18,
),
));
...can now help us find all those below a certain age.
The query can grow as complex as you wish with $conditions and $params, but with your normal method, you may only use assignment queries.