Delete from elasticsearch all items where field does not match a certan value - elasticsearch

I'm trying to delete all items from Elasticsearch index where field time_crawl_started does NOT match a specific value. I'm using match_all query in combination with NOT filter.
This is what I got so far:
$client = new Elasticsearch\Client();
$params = Array(
'index' => ...,
'type' => ...
);
$params['body']['query']['filtered']['query']['match_all'] = Array();
$params['body']['query']['filtered']['filter']['not']['term']['time_crawl_started'] = $someDate;
$client->deleteByQuery($params);
The problem is that this deletes all items, even ones having time_crawl_started set to $someDate, which is simply a datetime such as "2014-02-17 19:13:31".
How should I change this to delete only the items that don't have the correct date?

The problem was that time_crawl_started field was analyzed and thus any comparison by value was wrong. I had to create index manually (as opposed to automagically by just inserting a new document into non-existing index) and specify mapping for my item type, setting 'index' => 'not_analyzed' for time_crawl_started.
And I ended up using script filter like this:
$params['body']['query']['filtered']['query']['match_all'] = Array();
$params['body']['query']['filtered']['filter']['script']['script'] = "doc['time_crawl_started'].value != \"" . $someDate . "\"";

Related

Put specific item on top without sorting others in laravel collection

I have an ordered laravel collection and i need too put element with id = 20 on top, without sorting other elements. Is it possible to do with sortBy?
You can try to use filter method
// Say $originalCollection is the response from the large request, with data from the database
$modifiedCollection = $originalCollection->filter(fn($item) => $item->id === 20)
->concat($originalCollection->filter(fn($item) => $item->id !== 20));
Or to be more intuitive you can use filter and reject methods
$modifiedCollection = $originalCollection->filter(fn($item) => $item->id === 20)
->concat($originalCollection->reject(fn($item) => $item->id === 20));
The $modifiedCollection will have record with id = 20 at the top and rest of the records will remain in the same order as in $originalCollection
if you want to put a specific item at the top of the array, simply add it separately.
$type = ['20' => 'Select Type'] + $your_sorted_array ;
Example:
$country = ['1' => 'Andorra'] + Countries::orderby('nicename')->pluck('name', 'id')->toArray();
Edit 1:Given new information, the on way you could "manually" do this is by using a combination of unset and unshift AFTER the array is built from the collection.
$key_value = $country[20];
unset($country[20]);
array_unshift($country, $key_value );
if your collection is not very large you can use combination of keyBy, pull and prepend methods
$originalCollection = Model::hereYourBigQuery()->get()->keyBy('id');
/*
now collection will look like this
{
'id1' => objectWithId1,
'id2' => objectWithId2,
...
20 => objectWithId20,
...
}
*/
// pull takes off element by its key
$toMakeFirst = $originalCollection->pull(20);
// prepend adding item into begining of the collection
// note that prepend will reindex collection so its keys will be set by default
$originalCollection->prepend($toMakeFirst);
upd:
if you want to stick with sort there is a way
$collection = Model::yourBigQuery()->get();
$sorted = $collection->sort(function($a, $b){return $a->id == 20 ? -1 : 1;})->values();
as said in docs method sort can take closure as argument and utilizes php uasort under the hood

Laravel - what does the manual paginations 'option' query parameter do?

The manual pagination I found while googling works fine but I was just wondering what does the 'query' => $request->query() in the option parameter does?
$total = count($form_list);
$per_page = 10;
$current_page = $request->input('page') ?? 1;
$starting_point = ($current_page * $per_page) - $per_page;
$form_list = array_slice($form_list, $starting_point, $per_page, true);
$form_list = new Paginator($form_list, $total, $per_page, $current_page, [
'path' => $request->url(),
'query' => $request->query(),
]);
Calling ->query() without any parameters returns all the values from the query string as an associative array.
Suppose you have a query string like this:
https://example.com/path/to/page?name=ferret&color=purple
You can retrieve the value of name by doing something like so:
$request->query('name')
which returns ferret. You can also pass a second parameter for a default value so if you call:
$request->query('size', 'Medium')
which doesn't exist on the query string, you'll get 'Medium' instead of null.
If you omit all parameters, you'll receive an associative array that looks something like this:
query = [
'name' => 'ferret',
'color' => 'purple',
]
The options parameter is not needed by the pagination itself but for your dataset query. If you do not pass the query parameter, when you click one of the pagination urls, you'll get something like this:
https://example.com/path/to/page?page=2&per_page=5
Sometimes, this works fine and will give us something that we want but sometimes, we need those additional query string to get the correct dataset. We pass in all values from our query to get something like this:
https://example.com/path/to/page?page=2&per_page=5&name=ferret&color=purple
Which will filter your dataset for all those purple ferrets. As for the question if you need it, it's up for you to decide if that is essential for your code or if you can get away with just pagination.
Good luck! I hope this helps.

how to use increment function in laravel

i am using DB to store values in database.
i have "course fees" column i what to "increment" the "course_fees" value in column.
for example
DB::table('student')->where('registration_id','=', $request->registration_id)->increment(['course_fees' =>$request->course_fees]);
this code increment the inserted value
how can i modified below code for increment "course_fees" value like above
DB::table('student')->where('registration_id','=', $request->registration_id)->update(['payment_date' => $request->payment_date,'balance_fees' => $request->balance_fees,'course_fees' =>$request->course_fees]);
You cannot use this method to increment multiple fields. You can use:
$studentQuery = DB::table('student')->where('registration_id','=', $request->registration_id);
(clone $studentQuery)->increment('payment_date',$request->payment_date);
(clone $studentQuery)->increment('balance_fees', $request->balance_fees);
(clone $studentQuery)->increment('course_fees', $request->course_fees);
but this way you will run 3 database queries to update.
But if you are sure there is exactly single record found for registration_id you can do it like this:
$student = DB::table('student')->where('registration_id','=', $request->registration_id)->first();
$student->update([
'payment_date' => $student->payment_date + $request->payment_date,
'balance_fees' => $student->balance_fees + $request->balance_fees,
'course_fees' => $student->course_fees + $request->course_fees
]);
EDIT
If you want to increment only course_fees column and want to update other 2 columns from input you can use:
DB::table('student')->where('registration_id','=', $request->registration_id)
->increment('course_fees' , $request->course_fees, [
'payment_date' => $request->payment_date,
'balance_fees' => $request->balance_fees
])
This is documentation about increment/decrement methods.
increment()/decrement() can take 3 parameters: $column, $amount, $extra.
$column is the field that you want to increment
$amount is by how much you want to increment the field by
$extra is an array of attributes that you also want to update in the query.
If you don't pass an amount the default for $amount is 1.
To achieve what you're after you could do:
DB::table('student')
->where('registration_id', $request->registration_id)
->increment('course_fees', $request->course_fees, [
'payment_date' => $request->payment_date,
'balance_fees' => $request->balance_fees,
]);

NEST MultiGet search all types possible?

I have got unique document ids (across all types) and I would like to check which document already exists in elasticsearch index. I try to search
var duplicateCheck = _elasticClient
.MultiGet(m => m.GetMany<object>(notices.Select(s => s.Id)).Fields("Id"));
but it returns wrong result - every document has set found property to false.
update
there is workaround here
var exisitngDocIds = _elasticClient.Search<ZPBase>(s => s
.AllTypes()
.Query(q => q.Ids(notices.Select(z=>z.Id)))
.Fields("Id")
.Take(notices.Count)
);
notices = notices.Where(q => !exisitngDocIds.Hits.Any(s => s.Id == q.Id)).ToList();
From the Multi Get API documentation I realized that you can use something similar to the following code to solve your problem:
var response = _elasticClient.MultiGet(m => m
.Index(MyIndex)
.Type("")
.GetMany<ZPBase>(noticeIds));
Note the empty string passed as the Type.

how to add dynamic facets in Elasticsearch and Nest

i'm have a movies and music db. based on if the user is in movies or music, the facets need to change.
i've tried using .OnFields(string[]) to pass in an array (which changes based on movies/music) but i'm getting an error.
here's the code i'm using which generates the error. what am i missing?
string[] facetFields = new []{"genres","format","decades","price"};
var searchResult = client.Search<MyData>(s => s
.MatchAll()
.FacetTerm(t => t
.OnFields(facetFields)
.Order(TermsOrder.term)
.AllTerms()
));
the error is:
"Couldn't infer name for facet of type TermFacetDescriptor`1"
found the answer. you have to name the facet (i knew that but wasn't sure where) like so ...
see 'MyFacet' below...
var searchResult = client.Search<MyData>(s => s
.MatchAll()
.FacetTerm("MyFacet", t => t
.OnFields(facetFields)
.Order(TermsOrder.term)
.AllTerms()
));
if this is in the documentation somewhere - could someone point me to it?
Thanks!

Resources