elastic-search post value select all if nothing is selected - php

i am trying to filter data based on the user selection.
as a example if a user select a name "Smith" and "Type(Which is male of female)", it actually works fine.
The problem is if a user do not select any name and just select just type, than based on the type to should query the data, which means it should select all "names", instead of selecting a single names.
Now if the user do not select any name it returns null value in the query, so how can i make the query to act like when nothing is "post" it should "select all".
Do any one knows any solution for this problem !
if ($request->getMethod() == 'POST') {
$name = $request->request->get('name'); // get requested name
$type = $request->request->get('type'); // Which is male of female
$params = array(
'index' => "myIndex",
'type' => "myType",
'body' => array(
'query' => array(
'bool' => array(
'should' => array(
'query_string' => array(
'default_field' => 'name',
'query' => $name
)
)
)
),
'term' => array(
"type" => $type
)
)
);
$docs = $client->search($params);
For making it more clear how to make a condition for elasticserch if post value is null select all

I think you should simply build your query conditionally, i.e. add each constraint only if the related parameter is present in the request. Besides, you also have another issue in your query, i.e. the term query is misplaced, it should be located inside the bool/should and not directly in the body. So here is how I'd do it:
if ($request->getMethod() == 'POST') {
$name = $request->request->get('name'); // get requested name
$type = $request->request->get('type'); // Which is male of female
// create the base skeleton of your query
$params = array(
'index' => "myIndex",
'type' => "myType",
'body' => array(
'query' => array(
'bool' => array(
'should' => array(
// empty should clause for starters
)
)
)
)
);
// add each constraint in turn depending on whether the param is specified
if (!empty($name)) {
$params['body']['query']['bool']['should'] = array(
'query_string' => array(
'default_field' => 'name',
'query' => $name
)
);
}
if (!empty($type)) {
$params['body']['query']['bool']['should'] = array(
'term' => array(
"type" => $type
)
);
}
// special case if none is present, just match everything
if (count($params['body']['query']['bool']['should']) == 0) {
$params['body']['query'] => array(
'match_all' => array()
);
}
$docs = $client->search($params);

Related

Exact search query with Elastica QueryBuilder

I'm having a problem with an Elastica QueryBuilder exact search in a Symfony app. The Elastica version is 2.1, which depends on Elasticsearch 1.5.2. I'm searching an index where one of the fields is mapped as
majors:
type: string
index : not_analyzed
The field is a student's major that can have one or more words, such as "Ancient History".
The query is set up like this:
/** #var ElasticaFactory $ef */
protected $ef;
public function __construct(ElasticaFactory $ef)
{
$this->ef = $ef;
}
$query = $this->ef->createQuery();
$qb = $this->ef->createQueryBuilder();
$bool = $qb->filter()->bool();
$query->setQuery(
$qb->query()->filtered(
$qb->query()->term(),
$bool->addShould(
$qb->filter()->term(
array('majors' => $params['majors'])
)
)
)
);
When I run this query, I get a SearchPhaseExecutionException error. The full text of the error message is here.
Stack trace shows the full contents of the request:
Client ->request ('students/_search', 'GET', array('query' => array('filtered' => array('query' => array('term' => array()), 'filter' => array('bool' => array('should' => array(array('term' => array('majors' => 'Ancient Studies')))))))), array('from' => '0', 'size' => '10'))
in /vagrant/persona/vendor/ruflin/elastica/lib/Elastica/Search.php at line 455
When I set up the query as a "match" below, it works with no errors:
$query->setQuery(
$qb->query()->filtered(
$qb->query()->match('majors', $params['majors']),
$bool
)
);
However, a match query returns too many irrelevant results. I need specifically an exact search query.
Next, in order to eliminate any issues with Elastica, I converted the query into a raw ES query, like this:
$query->setRawQuery(
array(
'query' => array(
'filtered' => array(
'query' => array(
'term' => array()),
'filter' => array(
'bool' => array(
'should' => array(
array(
'term' => array(
'majors' => 'Art History'
)
)
)
)
)
)
)
)
);
This gave me the same error message as quoted above. Looks like there is a problem with the query itself, and not with Elastica.
When I rearranged the query to exactly follow the documentation as below, it returned no results, even though matching records were present
$query->setRawQuery(
array(
'query' => array(
'filtered' => array(
'filter' => array(
'term' => array(
'majors' => 'Art History'
)
)
)
)
)
);
Any help would be appreciated!

make two query work after each other

I am trying to find out the names between a time period.
But the problem is when i try to run it
first-- it returns the selected names value
second -- it returns data between that period.
Now what i want to do is that, i want to search name between a time period.
For explaining it more, suppose we have select a name "john", and select a date 2/08/2015 to 27/08/2015,
so it should return all the names between that time period.
But it is returning the names of john and than searching for other documents between that period, where is should also take the name "John" when searching the date !
How to solve this problem !
$name = $request->request->get('name');
$strat_date = $request->request->get('strat_date');
$end_date = $request->request->get('end_date');
$params = array(
'index' => "myIndex",
'type' => "myType",
'body' => array(
'query' => array(
'bool' => array(
'must' => array(
// empty should clause for starters
)
)
)
)
);
// add each constraint in turn depending on whether the param is specified
if (!empty($sourceFilter)) {
$params['body']['query']['bool']['must'] = array(
'query_string' => array(
'default_field' => 'name',
'query' => implode(" ", $name)
)
);
}
if (!empty($end_date)) {
$params['body']['query']['bool']['must']['range'] = array(
'datehistory' => array(
"from" => $strat_date,
"to" => $end_date
)
);
}
// special case if none is present, just match everything
if (count($params['body']['query']['bool']['must']) == 0) {
$params['body']['query'] = array(
'match_all' => array()
);
}
$docs = $client->search($params);
But at always return the an error like this ----
{"error":"SearchPhaseExecutionException[Failed to execute phase
[query], all shards failed; shardFailures
{[agEfJ6ltSJmlec3gpnPg3g][myIndex][1]:
SearchParseException[[myIndex][1]: from[-1],size[-1]: Parse Failure
[Failed to parse name
[{\"query\":{\"bool\":{\"must\":{\"query_string\":{\"default_field\":\"name\",\"query\":\"john.se\"},\"range\":{\"datehistory\":{\"from\":1438207200,\"to\":1440626400}}}}}}]]];
nested: QueryParsingException[[myIndex] No query registered for
[datehistory]]; }]","status":400}
You have a mix of bool/should and bool/must so you simply need to change bool/should to bool/must.
$params = array(
'index' => "myIndex",
'type' => "myType",
'body' => array(
'query' => array(
'bool' => array(
'must' => array( <--- CHANGE
// empty should clause for starters
)
)
)
)
);
// add each constraint in turn depending on whether the param is specified
if (!empty($sourceFilter)) {
$params['body']['query']['bool']['must'][] = array( <--- CHANGE
'query_string' => array(
'default_field' => 'name',
'query' => implode(" ", $name)
)
);
}
if (!empty($end_date)) {
$params['body']['query']['bool']['must'][] = array( <--- CHANGE
'range' => array(
'datehistory' => array(
"from" => $strat_date,
"to" => $end_date
)
)
);
}
// special case if none is present, just match everything
if (count($params['body']['query']['bool']['must']) == 0) { <-- CHANGE
$params['body']['query'] = array(
'match_all' => array()
);
}
$docs = $client->search($params);

Mailchimp API with Groupings

I need to add a MailChimp subscriber to a specific grouping. I have no issue getting the subscriber subscribed, but can't seem to get them into a specific grouping.
This is what I have:
// ENTER INTO MAILCHIMP
$newsletter = $_POST['newsletter'];
$newsletter = 'yes';
if ($newsletter = "yes") {
$MailChimp = new \Drewm\MailChimp('api');
$result = $MailChimp->call('lists/subscribe',
array(
'id' => 'listnumber',
'email' => array('email'=> $_POST['usersEmail']),
'merge_vars' => array(
'FNAME'=>$_POST['usersName'], 'LNAME'=>$_POST['usersLastName'],
'groupings' => array(
'id' => 494281,
array(
'name'=>'KeepBoard',
'groups' => 'KeepBoardUsers'
)
)
),
'double_optin' => false,
'update_existing' => true,
'replace_interests' => false,
'send_welcome' => false
));
print_r($result);
}
Also I am not entirely sure where to grab the listing id. I can't seem to find documentation. I worked off the URL. But I don't think that is right because that is the id for all groupings.
For those that have this problem this was the issue. The groups have to be an array, even for a single group.

Avoid duplicates by associating to inserted records with CakePHP saveMany

I am trying to take advantage of CakePHP's saveMany feature (with associated data feature), however am creating duplicate records. I think it is because the find() query is not finding authors, as the transaction has not yet been committed to the database.
This means that if there are two authors with the same username, for example, in the spreadsheet, then CakePHP will not associate the second with the first, but rather create two. I have made up some code for this post:
/*
* Foobar user (not in database) entered twice, whereas Existing user
* (in database) is associated
*/
$spreadsheet_rows = array(
array(
'title' => 'New post',
'author_username' => 'foobar',
'content' => 'New post'
),
array(
'title' => 'Another new post',
'author_username' => 'foobar',
'content' => 'Another new post'
),
array(
'title' => 'Third post',
'author_username' => 'Existing user',
'content' => 'Third post'
),
array(
'title' => 'Fourth post', // author_id in this case would be NULL
'content' => 'Third post'
),
);
$posts = array();
foreach ($spreadsheet_rows as $row) {
/*
* This query doesn't pick up the authors
* entered automatically (see comment 2.)
* within the db transaction by CakePHP,
* so creates duplicate author names
*/
$author = $this->Author->find('first', array('conditions' => array('Author.username' => $row['author_username'])));
$post = array(
'title' => $row['title'],
'content' => $row['content'],
);
/*
* Associate post to existing author
*/
if (!empty($author)) {
$post['author_id'] = $author['Author']['id'];
} else {
/*
* 2. CakePHP creates and automatically
* associates new author record if author_username is not blank
* (author_id is NULL in db if blank)
*/
if (!empty($ow['author_username'])) {
$post['Author']['username'] = $row['author_username'];
}
}
$posts[] = $post;
}
$this->Post->saveMany($posts, array('deep' => true));
Is there any way that this can be achieved, while also keeping transactions?
Update
You new requirement to save also posts that have no associated authors changes the situation a lot, as mentioned in the comments, CakePHPs model save methods are not ment to be able to save data from different models at once if it's not an association, if you need to do this in a transaction, then you'll need to handle this manually.
Save authors and their posts instead of posts and their authors
I would suggest that you save the data the other way around, that is save authors and their associated posts, that way you can easily take care of the duplicate users by simply grouping their data by using the username.
That way around CakePHP will create new authors only when neccessary, and add the appropriate foreign keys to the posts automatically.
The data should then be formatted like this:
Array
(
[0] => Array
(
[username] => foobar
[Post] => Array
(
[0] => Array
(
[title] => New post
)
[1] => Array
(
[title] => Another new post
)
)
)
[1] => Array
(
[id] => 1
[Post] => Array
(
[0] => Array
(
[title] => Third post
)
)
)
)
And you would save via the Author model:
$this->Author->saveMany($data, array('deep' => true));
Store non associated posts separately and make use of transactions manually
There is no way around this if you want to use the CakePHP ORM, just imagine what the raw SQL query would need to look like if it would need to handle all that logic.
So just split this into two saves, and use DboSource::begin()/commit()/rollback() manually to wrap it all up.
An example
Here's a simple example based on your data, updated for your new requirements:
$spreadsheet_rows = array(
array(
'title' => 'New post',
'author_username' => 'foobar',
'content' => 'New post'
),
array(
'title' => 'Another new post',
'author_username' => 'foobar',
'content' => 'Another new post'
),
array(
'title' => 'Third post',
'author_username' => 'Existing user',
'content' => 'Third post'
),
array(
'title' => 'Fourth post',
'content' => 'Fourth post'
),
array(
'title' => 'Fifth post',
'content' => 'Fifth post'
),
);
$authors = array();
$posts = array();
foreach ($spreadsheet_rows as $row) {
// store non-author associated posts separately
if (!isset($row['author_username'])) {
$posts[] = $row;
} else {
$username = $row['author_username'];
// prepare an author only once per username
if (!isset($authors[$username])) {
$author = $this->Author->find('first', array(
'conditions' => array(
'Author.username' => $row['author_username']
)
));
// if the author already exists use its id, otherwise
// use the username so that a new author is being created
if (!empty($author)) {
$authors[$username] = array(
'id' => $author['Author']['id']
);
} else {
$authors[$username] = array(
'username' => $username
);
}
$authors[$username]['Post'] = array();
}
// group posts under their respective authors
$authors[$username]['Post'][] = array(
'title' => $row['title'],
'content' => $row['content'],
);
}
}
// convert the string (username) indices into numeric ones
$authors = Hash::extract($authors, '{s}');
// manually wrap both saves in a transaction.
//
// might require additional table locking as
// CakePHP issues SELECT queries in between.
//
// also this example requires both tables to use
// the default connection
$ds = ConnectionManager::getDataSource('default');
$ds->begin();
try {
$result =
$this->Author->saveMany($authors, array('deep' => true)) &&
$this->Post->saveMany($posts);
if ($result && $ds->commit() !== false) {
// success, yay
} else {
// failure, buhu
$ds->rollback();
}
} catch(Exception $e) {
// failed hard, ouch
$ds->rollback();
throw $e;
}
You need to use saveAll, which is a mix between saveMany and saveAssociated (you will need to do both of them here).
Plus, you need to change the structure of each post.
Here is an example of the structures you will need to create inside the loop.
<?php
$posts = array();
//This is a post for a row with a new author
$post = array (
'Post' => array ('title' => 'My Title', 'content' => 'This is the content'),
'Author' => array ('username' => 'new_author')
);
$posts[] = $post;
//This is a post for a row with an existing author
$post = array (
'Post' => array ('title' => 'My Second Title', 'content' => 'This is another content'),
'Author' => array ('id' => 1)
);
$posts[] = $post;
//This is a post for a row with no author
$post = array (
'Post' => array ('title' => 'My Third Title', 'content' => 'This is one more content')
);
$posts[] = $post;
$this->Post->saveAll($posts, array ('deep' => true));
?>
Following the "use transactions manually" bit suggested by ndm, this piece of code (written in a unit test!) seemed to do the trick:
public function testAdd() {
$this->generate('Articles', array());
$this->controller->loadModel('Article');
$this->controller->loadModel('Author');
$csv_data = array(
array(
'Article' => array(
'title' => 'title'
)),
array(
'Article' => array(
'title' => 'title'
),
'Author' => array(
'name' => 'foobar'
),
),
array(
'Article' => array(
'title' => 'title2'
),
'Author' => array(
'name' => 'foobar'
)
),
/* array( */
/* 'Article' => array( */
/* 'title' => '' */
/* ), */
/* 'Author' => array( */
/* 'name' => '' // this breaks our validation */
/* ) */
/* ), */
);
$db = $this->controller->Article->getDataSource();
$db->begin();
/*
* We want to inform the user of _all_ validation messages, not one at a time
*/
$validation_errors = array();
/*
* Do this by row count, so that user can look through their CSV file
*/
$row_count = 1;
foreach ($csv_data as &$row) {
/*
* If author already exists, don't create new record, but associate to existing
*/
if (!empty($row['Author'])) {
$author = $this->controller->Author->find('first',
array(
'conditions' => array(
'name' => $row['Author']['name']
)
));
if (!empty($author)) {
$row['Author']['id'] = $author['Author']['id'];
}
}
$this->controller->Article->saveAssociated($row, array('validate' => true));
if (!empty($this->controller->Article->validationErrors)) {
$validation_errors[$row_count] = $this->controller->Article->validationErrors;
}
$row_count++;
}
if (empty($validation_errors)) {
$db->commit();
} else {
$db->rollback();
debug($validation_errors);
}
debug($this->controller->Article->find('all'));
}

MailChimp API PHP - Add to Interest Group

I'm currently using the MailChimp API for PHP, version 1.3.1 (http://apidocs.mailchimp.com/api/downloads/#php)
I've set up a list in MailChimp, and would like to dynamically add:
Subscribers to the list (done: $objMailChimp->listBatchSubscribe($strMailingListID, ...))
Interest Groupings (done: $objMailChimp->listInterestGroupingAdd($strMailingListID, ...))
Interest Groups into those Groupings (done: $objMailChimp->listInterestGroupAdd($strMailingListID, ...))
Subscribers assigned to relevant Groups (not done)
The API (http://apidocs.mailchimp.com/api/1.3/#listrelated) is somewhat unclear on how to add a subscriber to an interest group - does anyone here have any ideas?
As of version 2.0 of MailChimp's API, this should work:
$merge_vars = array(
'GROUPINGS' => array(
array(
'name' => "GROUP CATEGORY #1", // You can use either 'name' or 'id' to identify the group
'groups' => array("GROUP NAME","GROUP NAME")
),
array(
'name' => "GROUP CATEGORY #2",
'groups' => array("GROUP NAME")
)
)
);
Source: http://apidocs.mailchimp.com/api/2.0/lists/subscribe.php
Using a barebones PHP wrapper (https://github.com/drewm/mailchimp-api/) you can then send this to MailChimp via either the lists/subscribe or lists/batch-subscribe:
$MailChimp = new MailChimp('API_KEY');
$result = $MailChimp->call('lists/subscribe', array(
'id' => 'LIST ID',
'email' => array('email'=>'trevor#example.com'),
'merge_vars' => $merge_vars
));
For MailChimp API v3
As of v3, 'groupings' has changed to 'interests'.
You have to find out the ID of the group (interest) that you are wanting to add to. Unfortunately this cannot be found anywhere on the MailChimp dashboard.
The easiest way to find out the 'interest' ID (rather than creating a script) is to go to the MailChimp playground and then, after entering in your API key, route to...
lists > the list in question > interest-categories (in the sub-resources dropdown)
then...
interests (in the sub-resources dropdown) for interest category
then...
Click through to the interest and refer to the 'id' field, ignoring the other ID fields
OR
lists > the list in question > members (in the sub-resources dropdown)
then...
load (in the actions dropdown) for any member
or
Create Members (button)
The page will load the member's details. Scroll down until you see the 'interests' array/object. There you will see the IDs. Notice they can be set to true or false.
You will have to figure out which ID relates to what 'group'/'interest' by going about the previous method or making the call, and then looking at the member's details via your MailChimp dashboard.
So when it comes to actually making the POST call ('member' create), you would want something on the lines of...
{
"email_address":"example#freddiesjokes.com",
"status":"subscribed",
"interests": {
"b8a9d7cbf6": true,
"5998e44916": false
},
# ADDITIONAL FIELDS, IF REQUIRED...
"merge_fields":{
"FNAME": "foo bar",
"LNAME": "foo bar",
"MERGE3": "foo bar",
"MERGE4": "foo bar"
}
}
A PUT call ('member' edit) example...
{
"interests": {
"b8a9d7cbf6": false,
"5998e44916": true
}
}
It seems that you must declare every 'interest', and state whether it is true or false.
I could not get the other answers on this page to work. Here's the merge vars that I had to use:
$merge_vars = array(
'GROUPINGS' => array(
0 => array(
'id' => "101", //You have to find the number via the API
'groups' => "Interest Name 1, Interest Name 2",
)
)
);
Use GROUPINGS merge var:
Set Interest Groups by Grouping. Each element in this array should be
an array containing the "groups" parameter which contains a comma
delimited list of Interest Groups to add. Commas in Interest Group
names should be escaped with a backslash. ie, "," => "\," and either
an "id" or "name" parameter to specify the Grouping - get from
listInterestGroupings()
Here's the code I got to work
require_once 'MCAPI.class.php';
require_once 'config.inc.php'; //contains apikey
// use this once to find out id of interest group List
//$retval = $api->listInterestGroupings($listId);
//echo '<pre>';
//print_r($retval);
//echo '</pre>';
//die();
$emailAddress = 'info#example.com';
//You have to find the number via the API (id of interest group list)
$interestGroupListId = FILLMEIN;
$api = new MCAPI($apikey);
// Create an array of Interest Groups you want to add the subscriber to.
$mergeVars = array(
'GROUPINGS' => array(
0 => array(
'id' => $interestGroupListId,
'groups' => "FILL IN GROUP NAMES",
)
)
);
// Then use listUpdateMember to add them
$retval = $api->listUpdateMember($listId, $emailAddress, $mergeVars);
if ($api->errorCode){
echo "Unable to update member info!\n";
echo "\tCode=".$api->errorCode."\n";
echo "\tMsg=".$api->errorMessage."\n";
} else {
echo "Returned: ".$retval."\n";
}
This is a variation of Justins answer but having tried all the above this is the only one I could get to work with DrewM's MailChimp wrapper
$merge_vars = array(
'GROUPINGS' => array(
0 => array(
'id' => '[GROUP_LIST_ID]', // need grouping ID
'name' => '[OR_GROUP_LIST_NAME]', // or name instead
'groups' => array( '[GROUP_NAME_1]', '[GROUP_NAME_2]' )
)
),
);
$mc = new MailChimp('[YOUR_MAILCHIMP_API]');
$mc->call(
'lists/subscribe', array(
'id' => '[YOUR_LIST_ID]', // list ID
'email' => array( 'email' => 'someone#something.com'),
'merge_vars' => $merge_vars,
'double_optin' => true,
'update_existing' => true,
'replace_interests' => false, // do you want to add or replace?
'send_welcome' => false,
)
);
If you are unsure of your groups lists ID and wish to use it you can call:
$current_groupings = $mc->call( 'lists/interest-groupings', array(
'id' => '[GROUP_LIST_ID]',
) );
var_dump($current_groupings);
Also please note optional but very important last parameter of listUpdateMember, by default replace_interests it set to true so it will overwrite any subscription the user you are updating could have had in the past. If you want to add new ones not touching previous ones just pass the new group name you wanna add and set replace_interests to false.
$api = new MailChimp('API_KEY');
$pArray = array('GROUPINGS'=>array(
array('name'=>'GROUP CATEGORY', 'groups'=>'group1,group2'),
)
);
if (is_array($pArray)) {
$api->listUpdateMember($pListId, $pEmail, $pArray, '', false);
}
Example using DrewM's MailChimp wrapper and shorthand syntax for arrays (PHP 5.4+) add to groups:
$merge_vars = [
'GROUPINGS' => array(
0 => [
'id' => 12345, // need grouping ID
'name' => 'Interests', // or name instead
'groups' => [ 'cars', 'trucks' ]
]
]
];
$mc = new MailChimp('API_KEY');
$mc->call(
'lists/subscribe', [
'id' => '9876', // list ID
'email' => array[ 'email' => 'example#abc.com' ],
'merge_vars' => $merge_vars,
'double_optin' => true,
'update_existing' => true,
'replace_interests' => false, // do you want to add or replace?
'send_welcome' => false,
]
);
You need grouping 'name' OR 'id', both are not needed. To get grouping ID use: lists/interest-groupings

Categories