How to create a parent-child inner hit query with elasticsearchDSL Builder - php

Using the ONGR/ElasticsearchDSL I'm trying to add a Parent child inner hit query. The example documentation indicates that the proper way to do this is to
{
"inner_hits" : {
"children" : {
"type" : {
"article" : {
"query" : {
"match" : {"title" : "[actual query]"}
}
}
}
}
}
}
And now the query via DSL:
$matchQuery = new MatchQuery('title', '[actual query]');
$innerHit = new ParentInnerHit('children', 'article', $matchQuery);
$search = new Search();
$search->addInnerHit($innerHit);
$search->toArray();
So for my scenario I did:
$termQuery = new TermQuery('user', $query);
$innerHit = new ParentInnerHit('child_type', 'parent_type', $termQuery);
$search->addInnerHit($innerHit);
My problem is that I'm getting the error message:
Catchable fatal error: Argument 3 passed to
ONGR\ElasticsearchDSL\InerHit\NestedInnerHit::__construct()
must be an instance of ONGR\ElasticsearchDSL\Search,
instance of ONGR\ElasticsearchDSL\Query\TermLevel\TermQuery
give defined in ../ongr/elasticsearch-dsl/src/InnerHit/NestedInnerHit.php
on line 46
Any thoughts or suggestions?

As you can see from exception ParentInnerHit is expecting Search instead of Query, which makes sense. To build desired query you have to:
$termQuery = new TermQuery('user', $query);
$searchObject = new Search();
$search->addQuery($termQuery);
$innerHit = new ParentInnerHit('child_type', 'parent_type', $searchObject);
$search->addInnerHit($innerHit);
Did not test this, but you should get the idea.

Related

How to resolve Fatal error: Uncaught TypeError: Return value?

I have a very annoying problem to solve. I have a method that searches for a code of a sale, it is:
public function findByCode(string $code, string $columns = "*"): ?Sales
{
$find = $this->find("code = :code", "code={$code}", $columns);
return $find->fetch(true);
}
When I try to call him that:
$sales = (new Sales())->findByCode(client()->code);
It shows me the following error:
Uncaught TypeError: Return value of Source\Models\Sales::findByCode() must be an instance of Source\Models\Sales or null, array returned in
How to solve this?
You are making the findByCode function restrict to return data with type Sales and when you are using return $find->fetch(true); it will return some array of that's type is not compatible with Sales so PHP is complaining here about that. so just make a new object of Sales and return it.
The code should be some thing like this:
public function findByCode(string $code, string $columns = "*"): ?Sales
{
$find = $this->find("code = :code", "code={$code}", $columns);
$result = $find->fetch(true);
return new Sales($result);
}
So the Sales class can get the data and assign them to its properties and act as a DTO or something.

Laravel Override Exception on NULL Object Reference

For my API we are accepting loads of JSON data. Sometimes there is missing data when we refer to it. So given the following JSON that is posted to the API:
{
"reference_id": "6599",
"balance_0_30": "0",
"balance_31_60": "0",
"balance_over_90": "0",
"account_balance": "0"
}
As I loop over it like this:
foreach ($request->input('data') as $record) {
$record = (object) $record;
$accounting->reference_id = isset($record->reference_id) ? $record->reference_id : NULL;
$accounting->reference_guarantor_id = $record->reference_guarantor_id ?: NULL;
$accounting->balance_0_30 = isset($record->balance_0_30) ? $record->balance_0_30 : NULL;
$accounting->balance_31_60 = isset($record->balance_31_60) ? $record->balance_31_60 : NULL;
$accounting->balance_61_90 = isset($record->balance_61_90 ) ? $record->balance_61_90 : NULL;
$accounting->balance_over_90 = isset($record->balance_over_90) ? $record->balance_over_90 : NULL;
$accounting->account_balance = isset($record->account_balance) ? $record->account_balance : NULL;
This works, but it is rather "messy" to read, and I have about 4000 lines of similar code and growing.
The issue is that if I send up JSON data without the account_balance declared, I get the error:
Undefined property: stdClass
I was thinking I could write a tiny function like this:
function i($value) {
if($value!=null){
if(is_int($value)){
return $value;
}
if(is_float($value)){
return $value;
}
}
return 0;
}
Where if I knew that column would be an integer or float, I could call it like this:
$accounting->account_balance = i($record->account_balance);
Then if the value was null, it would just fill in a 0 and not error out. That would make things much easier to read, troubleshoot and so on. Trouble is that the Exception is thrown before it gets to the i function.
I tried using the set_exception_handler() as described here, including the class example from Glen: https://www.php.net/manual/en/function.set-exception-handler.php but it didn't work.
Am I out of luck, or is there a way to do what I want?
You could just do this:
foreach ($request->input('data') as $record) {
Accounting::create($record);
}
Your database columns should be nullable and do not forget to set $fillable attribute in your Accounting model (For this you can set protected $guarded = ['id', 'created_at', 'updated_at']; in your model to consider all other columns as fillable).

Uncaught Error: Call to undefined method MongoDB\\Collection::update()

I updated a environment. I upgraded PHP and Mongo. The code was using the legacy driver but now I'm using mongodb. I'm also using mongo-php-library. This code snippet is broken now and I'm not sure how to fix it. I've read about updateOne and replaceOne but I'm not sure how to use it:
function update($collection,$criteria,$data,$insertIfNotExists = false)
{
if (!isset($this->collection[$collection])) {
$this->collection[$collection] = self::$database->selectCollection($collection);
}
if ($insertIfNotExists) {
$oldData = $this->collection[$collection]->findOne($criteria);
if ($oldData == NULL) {
$data['createdDate'] = date("Y-m-d H:i:s");
$data['modifiedDate'] = (isset($data['modifiedDate'])) ? $data['modifiedDate']:date("Y-m-d H:i:s");
return ($this->collection[$collection]->insert($data)) ? array('status'=>'ok'):array('status'=>'error','error'=>'unknown_error');
} else {
$newData = $oldData;
foreach($data as $n=>$v) {
$newData[$n] = $v;
}
$newData['modifiedDate'] = (isset($newData['modifiedDate'])) ? $newData['modifiedDate']:date("Y-m-d H:i:s");
return ($this->collection[$collection]->update($criteria,$newData)) ? array('status'=>'ok'):array('status'=>'error','error'=>'unknown_error');
}
} else {
return ($this->collection[$collection]->update($criteria,$data)) ? array('status'=>'ok'):array('status'=>'error','error'=>'unknown_error');
}
}
The new driver changed the name of some methods. Instead of update() (that was used to update one or more documents), now you have updateOne() and updateMany(). The same applies to the other legacy methods insert() and remove(). You can get all further information you need on these changes in PHP MongoDB's Extension Docs and in MongoDB PHP Library.
So, just changing to the code below would fix that error:
$this->collection[$collection]->updateOne($criteria,$data)
Edit
Seems that you only need to update the field modifiedDate, so you can do the following:
$criteria = ['number' => '999'];
$newData = ['modifiedDate' => '2019-07-11 03:00:00'];
$this->collection[$collection]->updateOne(
$criteria,
'$set' => $newData
);
More info here: Update One Document

Gridfield not populate with ArrayList when not in public function getCMSFields

When I create GridField within admin console - everything is ok - I cam populate gridfield via classic method (ex. Member::get() - - or via ArrayList -
$al1 = new ArrayList();
$records = DB::query("SELECT * from Member where id<10");
while ($rec = $records->next()) {
$al1->push(new ArrayData($rec));
}
$grid = new GridField('Pages', 'All pages', $al1)
Both methods are working ok.
However, If I try to create GridField on user page - - presented in a form - - somehow the second method (where GridField should be populated by ArrayList - is not working).
$gridField = new GridField('pages1', 'All pages1', Member::get(), $config);
- woks ok, but the method where I create ArrayList old-fashioned way:
$al = new ArrayList();
$records = DB::query("SELECT * from Member where id<10");
while ($rec1 = $records->next()) {
$al->push(new ArrayData($rec));
}
I get an error when I try to render gridfield through:
return new Form($this, "AllSubmissions", new FieldList($gridField), new FieldList());
The error I am getting is:
[Warning] Missing argument 1 for ArrayData::__construct() GET /ss340/gridfield-test/gridfield-underr-grid/ Line 27 in C:\wamp\www\ss340\framework\view\ArrayData.php
Since I need data from external database to populate gridfield on non admin pages, I am desperate to get the solution for this.
If someone can provide me alternative method to show/edit tabular data in Silverstripe - -would appreciate very much.
I just looked up your error. It comes from the gridfield that tries to use this function:
public function getDisplayFields($gridField) {
if(!$this->displayFields) {
return singleton($gridField->getModelClass())->summaryFields();
}
return $this->displayFields;
}
If you are giving an ArrayList with ArrayData in it, it is trying to create a singleton of ArrayData. This causes an error because ArrayData expects an object or an array.
My oppinion is still to use my old answer, this would give you a DataList and you won't have to go through the trouble.
Old Answer
Why go through all the trouble and not just make use of SilverStripe's ORM with SearchFilters?
$dbConfig = [
"type" => 'MySQLDatabase',
"server" => 'localhost',
"username" => '',
"password" => '',
"database" => '',
"path" => '',
]; // fill this array with your other database configuration
//connect
DB::connect($dbConfig);
$members = Member::get()->filter('ID:LessThan', 10);
//reset to your own database
global $databaseConfig;
DB::connect($databaseConfig);
One last note; when 'developing' it is recommended to put SilverStripe in 'dev mode'. In the comments you say you are getting a Server Error (500) which indicates your SilverStripe is not in dev mode, or your error_reporting is not enabled. Maybe this could help you doing that.
OK, from the error you posted:
* var array
* see ArrayData::_construct() / protected $array;
/*
* #param object|array $value An associative array, or an object with simple properties.
* Converts object properties to keys of an associative array.
*/
public function __construct($value) {
if (is_object($value)) {
$this->array = get_object_vars($value);
} elseif (ArrayLib::is_associative($value)) {
$this->array = $value;
} elseif (is_array($value) && count($value) === 0) {
$this->array = array();
This error looks incomplete, but I noticed your submitted code is:
$al = new ArrayList();
$records = DB::query("SELECT * from Member where id<10");
while ($rec1 = $records->next()) {
$al->push(new ArrayData($rec));
}
But $rec is not defined and so likely you are not passing a valid constructor argument to new ArrayData()
try:
$al = new ArrayList();
$records = DB::query("SELECT * from Member where id<10");
while ($rec = $records->next()) {
$al->push(new ArrayData($rec));
}

Stored Javascript in MongoDB

I am trying to store a query in MongoDB by using stored javascript.
It looks something like this right now:
$collection = $db->$app_id;
$query = $collection->find( $filterQuery );
$db->system->js->save(array(
"_id" => "qo8qu0",
"value" => new MongoCode("function() { return array($query) }")
));
Then I execute it:
echo "<pre>";
print_r($test = $db->execute("qo8qu0()"));
echo "</pre>";
However, I am getting this error:
Catchable fatal error: Object of class MongoCursor could not be converted to string in /var/www/dev/classes/EmailMessaging.php on line 91
Line 91 is this line (like above),
"value" => new MongoCode("function() { return array($filterQuery) }")
If I use count( $filterQuery ); instead of find( $filterQuery );
It works and returns the right number.
How can I make it work so it returns the array when using find?
Thank you
It is because of evaluation of your code. It is evaluating this:
$query = $collection->find( $filterQuery );
So that $query is a MongoCursor and then you are attempting to concatenate that class into a string:
new MongoCode("function() { return array($query) }")
You need to do the entire thing witthin eval like so:
return $this->execute('function(){ return db.collection.find(filter); }', array('filter'=>array('_id'=>1)));
count works because it returns an int which can be added to the string.
Edit
A way:
$db->system->js->save(array(
"_id" => "qo8qu0",
"value" => new MongoCode("function() { return db.collection.find(filter).toArray() }")
));
And then in your app:
$test = $db->execute("qo8qu0()", array('filter'=>array('_id'=>1)))

Categories