View | Details | Raw Unified | Return to bug 24555
Collapse All | Expand All

(-)a/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm (-53 / +100 lines)
Lines 85-107 sub build_query { Link Here
85
    $query = '*' unless defined $query;
85
    $query = '*' unless defined $query;
86
86
87
    my $res;
87
    my $res;
88
    my $fields = $self->_search_fields({
88
    $res->{query} = $query;
89
        is_opac => $options{is_opac},
90
        weighted_fields => $options{weighted_fields},
91
    });
92
    if ($options{whole_record}) {
93
        push @$fields, 'marc_data_array.*';
94
    }
95
    $res->{query} = {
96
        query_string => {
97
            query            => $query,
98
            fuzziness        => $fuzzy_enabled ? 'auto' : '0',
99
            default_operator => 'AND',
100
            fields           => $fields,
101
            lenient          => JSON::true,
102
            analyze_wildcard => JSON::true,
103
        }
104
    };
105
89
106
    if ( $options{sort} ) {
90
    if ( $options{sort} ) {
107
        foreach my $sort ( @{ $options{sort} } ) {
91
        foreach my $sort ( @{ $options{sort} } ) {
Lines 195-217 sub build_query_compat { Link Here
195
            };
179
            };
196
        }
180
        }
197
181
198
        # We build a string query from limits and the queries. An alternative
182
199
        # would be to pass them separately into build_query and let it build
200
        # them into a structured ES query itself. Maybe later, though that'd be
201
        # more robust.
202
        $search_param_query_str = join( ' ', $self->_create_query_string(@search_params) );
203
        $query_str = join( ' AND ',
204
            $search_param_query_str || (),
205
            $self->_join_queries( $self->_convert_index_strings(@$limits) ) || () );
206
207
        # If there's no query on the left, let's remove the junk left behind
208
        $query_str =~ s/^ AND //;
209
        my %options;
183
        my %options;
210
        $options{sort} = \@sort_params;
184
        $options{sort} = \@sort_params;
211
        $options{is_opac} = $params->{is_opac};
185
        $options{is_opac} = $params->{is_opac};
212
        $options{weighted_fields} = $params->{weighted_fields};
186
        $options{weighted_fields} = $params->{weighted_fields};
213
        $options{whole_record} = $params->{whole_record};
187
        $options{whole_record} = $params->{whole_record};
214
        $query = $self->build_query( $query_str, %options );
188
189
        my $fields = $self->_search_fields({
190
            is_opac => $options{is_opac},
191
            weighted_fields => $options{weighted_fields},
192
        });
193
        if ($options{whole_record}) {
194
            push @$fields, 'marc_data_array.*';
195
        }
196
197
        my $bool_query = {
198
            bool => {
199
                minimum_should_match => 1,
200
                should => [
201
                    {
202
                        bool => {
203
                            must => []
204
                        }
205
                    }
206
                ]
207
            }
208
        };
209
210
        foreach my $search_param (@search_params) {
211
            next if $search_param->{operand} eq '*';
212
            my $operator = $search_param->{operator} // '';
213
            my $expression = $self->_create_match_expression($search_param, \%options);
214
            if (defined($expression->{multi_match})) {
215
                $expression->{multi_match}->{fields} = $fields;
216
            }
217
218
            if ($operator eq 'OR') {
219
                push @{ $bool_query->{bool}->{should} }, {
220
                    bool => {
221
                        must => [$expression],
222
                    },
223
                };
224
            } else {
225
                push @{ $bool_query->{bool}->{should}->[-1]->{bool}->{must} }, $expression;
226
            }
227
        }
228
229
        unless (@{ $bool_query->{bool}->{should}->[0]->{bool}->{must} }) {
230
            $bool_query->{bool}->{should}->[0] = { match_all => {} };
231
        }
232
233
        if (@$limits) {
234
            $bool_query->{bool}->{filter} = $self->_build_limit_filters($limits);
235
        }
236
237
        $query = $self->build_query( $bool_query, %options );
215
    }
238
    }
216
239
217
    # We roughly emulate the CGI parameters of the zebra query builder
240
    # We roughly emulate the CGI parameters of the zebra query builder
Lines 246-251 sub build_query_compat { Link Here
246
    );
269
    );
247
}
270
}
248
271
272
sub _create_match_expression {
273
    my ($self, $search_param) = @_;
274
275
    my $expression = {};
276
    unless (defined($search_param->{field})) {
277
        $expression->{multi_match} = {
278
            query => $search_param->{operand},
279
            lenient => JSON::true
280
281
        };
282
        return $expression;
283
    }
284
285
    my $field = $search_param->{field};
286
    $expression->{match} = {
287
        $field => $search_param->{operand}
288
    };
289
290
    return $expression;
291
}
292
249
=head2 build_authorities_query
293
=head2 build_authorities_query
250
294
251
    my $query = $builder->build_authorities_query(\%search);
295
    my $query = $builder->build_authorities_query(\%search);
Lines 735-740 sub _convert_index_fields { Link Here
735
    } @indexes;
779
    } @indexes;
736
}
780
}
737
781
782
=head2 _build_limit_filters
783
   my @filters = $self->_build_limit_filters
784
785
Transform limit strings to Elasticsearch filters
786
787
=cut
788
789
sub _build_limit_filters {
790
    my ( $self, $limits ) = @_;
791
    my @res;
792
    foreach my $s (@$limits) {
793
        next if $s eq '';
794
        my ( $field, $term ) = $s =~ /^\s*([\w,-]*?):(.*)/;
795
        next unless ( defined($field) && defined($term) );
796
797
        my ($conv) = $self->_convert_index_fields($field);
798
        my $facet_name = $conv->{field} . '__facet';
799
        push @res, {
800
            term => {
801
                $facet_name => $term
802
            }
803
        };
804
    }
805
806
    return \@res;
807
}
808
738
=head2 _convert_index_strings
809
=head2 _convert_index_strings
739
810
740
    my @searches = $self->_convert_index_strings(@searches);
811
    my @searches = $self->_convert_index_strings(@searches);
Lines 880-908 sub _make_phrases { Link Here
880
    map { s/^\s*(\w*?:)(.*)$/$1"$2"/r } @parts;
951
    map { s/^\s*(\w*?:)(.*)$/$1"$2"/r } @parts;
881
}
952
}
882
953
883
=head2 _create_query_string
884
885
    my @query_strings = $self->_create_query_string(@queries);
886
887
Given a list of hashrefs, it will turn them into a lucene-style query string.
888
The hash should contain field, type (both for the indexes), operator, and
889
operand.
890
891
=cut
892
893
sub _create_query_string {
894
    my ( $self, @queries ) = @_;
895
896
    map {
897
        my $otor  = $_->{operator} ? $_->{operator} . ' ' : '';
898
        my $field = $_->{field}    ? $_->{field} . ':'    : '';
899
900
        my $oand = $self->_modify_string_by_type(%$_);
901
        $oand = "($oand)" if $field && scalar(split(/\s+/, $oand)) > 1 && (!defined $_->{type} || $_->{type} ne 'st-year');
902
        "$otor($field$oand)";
903
    } @queries;
904
}
905
906
=head2 _clean_search_term
954
=head2 _clean_search_term
907
955
908
    my $term = $self->_clean_search_term($term);
956
    my $term = $self->_clean_search_term($term);
909
- 

Return to bug 24555