I'm having trouble doing multi-field sort script in elasticsearch , for example I want to sort by field A desc, field B desc. When I do the script with two sort it is doing sort only by field B desc.
'sort': [
{
'_script' : {
'script' : 'if (doc['+'\''+sortColumn1+'\''+'].value==null) {return '+'\''+'\''+'} else {return doc['+'\''+sortColumn1+'\''+'].value} ',
'type' : sortType1,
'order' : sortOrder1,
},
'_script' : {
'script' : 'if (doc['+'\''+sortColumn2+'\''+'].value==null) {return '+'\''+'\''+'} else {return doc['+'\''+sortColumn2+'\''+'].value} ',
'type' : sortType2,
'order' : sortOrder2
}
}
]
According to the official documentation for script sorting your JSON would have to looke like this:
'sort': {
'_script' : {
'script' : 'if (doc['+'\''+sortColumn1+'\''+'].value==null) {return '+'\''+'\''+'} else {return doc['+'\''+sortColumn1+'\''+'].value} ',
'type' : sortType1,
'order' : sortOrder1,
},
'_script' : {
'script' : 'if (doc['+'\''+sortColumn2+'\''+'].value==null) {return '+'\''+'\''+'} else {return doc['+'\''+sortColumn2+'\''+'].value} ',
'type' : sortType2,
'order' : sortOrder2
}
}
And it's clear why sorting does only work for field B. Because you override the key _script and the last one is the one which is taken into effect. So you can only define one _script to sort your result.
So you have to combine those two into one somehow.
Related
My View
Check This Image First
My Filter
Hello all I am trying to change this highlight field value using hook_views_query_alter....but I am unable to do... Can Someone Help me on this Query Alteration I am writing this code but it's not working
foreach ($query->where as &$condition_group) {
foreach ($condition_group['conditions'] as &$condition) {
if ($condition['field'] == 'paragraphs_item_field_data_node__field_recommendation_tagging__paragraph__field_recommendation_tag.field_recommendation_tag_target_id') {
$condition = [
'value' => $rec_tags,
'operator' => 'in',
];
}
}
}
First of all do you enter in your condition?
Verify the fieldname :
paragraphs_item_field_data_node__field_recommendation_tagging__paragraph__field_recommendation_tag.field_recommendation_tag_target_id
If you enter in your condition don't forget to re-add you field in your condition and that your value correspond to the right id :
foreach ($query->where as $condition_group) {
foreach ($condition_group['conditions'] as $condition) {
if ($condition['field'] === $field_name ) {
$condition = [
'field' => $field_name,
'value' => $rec_tags,
'operator' => 'in',
];
}
}
}
I have a hash that looks like
'Jarrod' => {
'Age' => '25 ',
'Occupation' => Student
},
'Elizabeth' => {
'Age' => '18',
'Occupation' => Student
},
'Nick' => {
'Age' => '32 ',
'Occupation' => Lawyer
},
I am trying to sort them by age so it will look like
'Nick' => {
'Age' => '32 ',
'Occupation' => Lawyer
},
'Jarrod' => {
'Age' => '25 ',
'Occupation' => Student
},
'Elizabeth' => {
'Age' => '18',
'Occupation' => Student
},
But I can't seem to figure out how to access anything past Age. How can I access the value of a value when ordering hash keys?
A hash variable %h with the shown data can be processed in the desired order as
use Data::Dump qw(pp); # to print out a complex data structure
say "$_->[0] => ", pp($_) for
map { [ $_, $h{$_} ] }
sort { $h{$b}->{Age} <=> $h{$a}->{Age} }
keys %h;
what prints (from a complete program below)
Nick => { Age => "32 ", Occupation => "Lawyer" }
Jarrod => { Age => "25 ", Occupation => "Student" }
Elizabeth => { Age => 18, Occupation => "Student" }
Note though that we cannot "sort a hash" and then have it be that way, as hashes are inherently random with order.† But we can of course go through and process the elements in a particular order, as above for example.
Explanation: sort takes pairs of elements of a submitted list in turn, available in variables $a and $b, and then runs the block of code we provide, so to compare them as prescribed. Here we have it compare, and thus sort, the elements by the value at key Age.
The output, though, is just those keys sorted as such! So we then pass that through a map, which combines each key with its hashref value and returns those pairs, each in an arrayref. That is used to print them, as a place holder for the actual processing.
A complete program
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd pp);
my %h = (
'Jarrod' => {
'Age' => '25 ',
'Occupation' => 'Student'
},
'Elizabeth' => {
'Age' => '18',
'Occupation' => 'Student'
},
'Nick' => {
'Age' => '32 ',
'Occupation' => 'Lawyer'
},
);
say "$_->[0] => ", pp($_->[1]) for
map { [ $_, $h{$_} ] } sort { $h{$b}->{Age} <=> $h{$a}->{Age} } keys %h;
Or, for a workable template, change to something like
my #sorted_pairs =
map { [ $_, $h{$_} ] } sort { $h{$b}->{Age} <=> $h{$a}->{Age} } keys %h;
for my $key_data (#sorted_pairs) {
say $key_data->[0], ' => ', pp $key_data->[1]; # or just: dd $key_data;
# my ($name, $data) = #$key_data;
# Process $data (a hashref) for each $name
}
Once we start building more suitable data structures for ordered data then there are various options, including one-person hashrefs for each name, stored in an array in the right order. Ultimately, all that can be very nicely organized in a class.
Note how Sort::Key makes the sorting part considerably less cumbersome
use Sort::Key qw(rnkeysort); # rn... -> Reverse Numerical
my #pairs = map { [ $_, $h{$_} ] } rnkeysort { $h{$_}->{Age} } keys %h;
The more complex -- or specific -- the sorting the more benefit from this module, with its many specific functions for generic criteria.
If these ages are given as integers then there is ikeysort (and rikeysort) and then those are hopefully unsigned integers, for which there is ukeysort (and rukeysort).
† See keys and perlsec, for example.
You can't sort a hash. A hash's elements are inherently unordered.
If you just want to visit the elements of hash in order, you can do that by getting and sorting the keys.
for my $name (
sort { $people{ $b }{ age } <=> $people{ $a }{ age } }
keys( %people )
) {
my $person = $people{ $name };
...
}
or
use Sort::Key qw( rikeysort );
for my $name (
rikeysort { $people{ $_ }{ age } }
keys( %people )
) {
my $person = $people{ $name };
...
}
If you need an ordered structure, you could start by converting the data to an array of people.
my #unordered_people =
map { +{ name => $_, %{ $people{ $_ } } }
keys( %people );
Then sorting that.
my #ordered_people =
sort { $b->{ age } <=> $a->{ age } }
#unordered_people;
or
use Sort::Key qw( rikeysort );
my #ordered_people =
rikeysort { $_->{ age } }
#unordered_people;
What does this bug refer to? I have pasted the code below. Kindly have a look. Can anyone let me know what's wrong with the boundary value here? Thanks in advance
db.match_list.aggregate(
[
{
$bucket: {
groupBy: "$competition_id",
boundaries: ["9dn1m1gh41emoep","9dn1m1ghew6moep", "d23xmvkh4g8qg8n","gy0or5jhj6qwzv3"],
default: "Other",
output: {
"data" : {
$push: {
"season_id": "$season_id",
"status_id": "$status_id",
"venue_id": "$venue_id",
"referee_id": "$referee_id",
"neutral":"$neutral",
"note": "$note",
"home_scores":"$home_scores",
"away_scores": "$away_scores",
}
}
}
}
},
{
$sort : { competition_id : 1 }
},
])
mongodb laravel query using raw. Not sure what's going wrong here.new_array vale also has been mentioned
$contents = $query->orderby('competition_id')->pluck('competition_id')->toArray();
$contents = array_unique($contents);
$new_array = array_values($contents);
$data = $query->raw(function ($collection) use ($new_array) {
return $collection->aggregate([
[
'$bucket' => [
'groupBy' => '$competition_id',
'boundaries' => $new_array,
'default' => 'zzzzzzzzzzzzzzzzzzzzzzzzz',
'output' => [
"data" => [
'$push' => [
"id" => '$id',
"season_id" => '$season_id',
"status_id" => '$status_id',
"venue_id" => '$venue_id',
"referee_id" => '$referee_id',
"neutral" => '$neutral',
"note" => '$note',
"home_scores" => '$home_scores',
]
]
]
]
]
]);
});
You have entered Other in the default parameter of the $bucket parameter which
is in between the min-max boundaries you have provided.
I would suggest you try a value of the greater or lesser string than to that provided in the
boundaries array or better enter a different datatype value such as int of 1.
db.match_list.aggregate( [
{
$bucket: {
groupBy: "$competition_id",
boundaries: ["9dn1m1gh41emoep","9dn1m1ghew6moep", "d23xmvkh4g8qg8n","gy0or5jhj6qwzv3"],
default: 1, // Or `zzzzzzzzzzzzzzzzzzzzzzzzz` (any greater or lesser value than provided in `boundaries`
output: {
"data" :
{
$push: {
"season_id": "$season_id",
"status_id": "$status_id",
"venue_id": "$venue_id",
"referee_id": "$referee_id",
"neutral":"$neutral",
"note": "$note",
"home_scores":"$home_scores",
"away_scores": "$away_scores",
}
}
}
}
},
{ $sort : { competition_id : 1 } },
] )
Elasticsearch: v7.2
Application: PHP - Laravel v5.7
Hello and good day!
I'm developing a web application that is similar to a search engine, whereas a user will enter words that will be designated to the variable $keywords. Then I have this query to search throughout my index:
$params = [
'index' => 'my_index',
'type' => 'my_type',
'from' => 0,
'size' => 10,
'body' => [
"query" => [
'bool' => [
'must' => [
[
"query_string" => [
"fields" => ['title','content'],
"query" => $keywords
]
]
]
]
]
]
];
$articles = $client->search($params);
Now, in line with my previous post, I was able to count the number of occurrences my $keywords occurred within the documents of my index.
Here's my highlight query that is attached to the $params above:
"highlight" => [
"fields" => [
"content" => ["number_of_fragments" => 0],
"title" => ["number_of_fragments" => 0]
]
'require_field_match' => true
]
Even though that the $keywords are enclosed with double quotation mark ("), the highlighter still chops/separates the $keywords and I already specified them with double quotation mark to strictly follow these words.
For example, my $keywords contains "Ayala Alabang", but as I displayed the output, it goes like this
The $keywords were separated, but according to the output, they're just adjacent to each other.
Is there any other tweaks or revision to my query? I found some related posts or questions in some forums, their last reply was from March 2019, any advice would be an excellent help for this dilemma
After a few days of looking into deep documentation, I found a way to properly segregate keywords that are found in a document
STEP 1
Apply the "explain" => true in your $params
$params = [
'index' = "myIndex",
'type' => "myType",
'size' => 50,
'explain' => true,
'query' => [
'match_all' => [
//your elasticearch query here
]
]
]
STEP 2
Then fetch the result after doing the $client->search($params) code:
$result = $client->search($params);
Then a long literal EXPLANATION will be included in your $result whereas your keywords and their frequency will be displayed in a text format.:
try displaying via dd($result['explanation'])
NOTE the problem here is that a lot of nested arrays will be the contents of the _explanation array key, so we came up with a recursive function to look for the keywords and their frequency
STEP 3
You need to create a function that will get a string IN BETWEEN of repetitive or other strings:
public static function get_string_between($string, $start, $end){
$string = ' ' . $string;
$ini = strpos($string, $start);
if ($ini == 0) return '';
$ini += strlen($start);
$len = strpos($string, $end, $ini) - $ini;
return substr($string, $ini, $len);
}
STEP 4
Then create the recursive function:
public static function extract_kwds($expln,$kwds)
{
foreach($expln as $k=>$v)
{
if($k == 'description' && strpos(json_encode($v),'weight(')!==false)
{
if(isset($kwds[$this->get_string_between($v,':',')')]))
{
$kwds[$this->get_string_between($v,':',')')] += intVal($this->get_string_between($expln['details'][0]['description'],'score(freq=',')'));
}
else
{
$kwds[$this->get_string_between($v,':',')')] = intVal($this->get_string_between($expln['details'][0]['description'],'score(freq=',')'));
}
}
if($k == 'details' && count($v) != 0)
{
foreach($v as $k2=>$v2)
{
$kwds = $this->extract_kwds($v2,$kwds);
}
}
}
return $kwds;
}
FINALLY
I was able to fetch all the keywords together with their frequency or how many times these keywords appeared in the documents.
This is similar to this question here but I can't figure out how to convert it to Mongoid syntax:
MongoDB query based on count of embedded document
Let's say I have Customer: {_id: ..., orders: [...]}
I want to be able to find all Customers that have existing orders, i.e. orders.size > 0. I've tried queries like Customer.where(:orders.size.gt => 0) to no avail. Can it be done with an exists? operator?
I nicer way would be to use the native syntax of MongoDB rather than resort to rails like methods or JavaScript evaluation as pointed to in the accepted answer of the question you link to. Especially as evaluating a JavaScript condition will be much slower.
The logical extension of $exists for a an array with some length greater than zero is to use "dot notation" and test for the presence of the "zero index" or first element of the array:
Customer.collection.find({ "orders.0" => { "$exists" => true } })
That can seemingly be done with any index value where n-1 is equal to the value of the index for the "length" of the array you are testing for at minimum.
Worth noting that for a "zero length" array exclusion the $size operator is also a valid alternative, when used with $not to negate the match:
Customer.collection.find({ "orders" => { "$not" => { "$size" => 0 } } })
But this does not apply well to larger "size" tests, as you would need to specify all sizes to be excluded:
Customer.collection.find({
"$and" => [
{ "orders" => { "$not" => { "$size" => 4 } } },
{ "orders" => { "$not" => { "$size" => 3 } } },
{ "orders" => { "$not" => { "$size" => 2 } } },
{ "orders" => { "$not" => { "$size" => 1 } } },
{ "orders" => { "$not" => { "$size" => 0 } } }
]
})
So the other syntax is clearer:
Customer.collection.find({ "orders.4" => { "$exists" => true } })
Which means 5 or more members in a concise way.
Please also note that none of these conditions alone can just an index, so if you have another filtering point that can it is best to include that condition first.
Just adding my solution which might be helpful for someone:
scope :with_orders, -> { where(orders: {"$exists" => true}, :orders.not => {"$size" => 0}}) }