|
| 1 | +# Changelog |
| 2 | + |
| 3 | +## v2 — full DSL coverage |
| 4 | + |
| 5 | +This branch brings every documented Elasticsearch DSL feature under typed PHP objects, fixes two queries that produced invalid DSL, and round-trips every feature against a real ES container via the new `AbstractElasticTestCase`. |
| 6 | + |
| 7 | +### Test infrastructure |
| 8 | + |
| 9 | +- New `tests/SpameriTests/ElasticQuery/AbstractElasticTestCase` base class with `createIndex($mapping)`, `indexDocument($body, $id, $refresh)`, `search($elasticQuery)`, `deleteIndex()`, and a generic `request($method, $path, $body)`. Tests that extend it shrink from ~70 lines of curl boilerplate to ~15. |
| 10 | + |
| 11 | +### Bug fixes (BC-breaking) |
| 12 | + |
| 13 | +| File | Previous | Fixed | |
| 14 | +| --- | --- | --- | |
| 15 | +| `Query/GeoDistance` | Emitted `{pin: {location: ...}}` (invalid DSL) and lacked the required `distance` argument. | Emits proper `geo_distance` envelope. Constructor now takes `distance` (required), plus `distance_type`, `validation_method`, `ignore_unmapped`, `boost`. | |
| 16 | +| `Query/Nested` | Wrapped inner query in `[$queryArray]` (extra list level — rejected by ES). | Inner query is now an object. Added `score_mode`, `ignore_unmapped`, `inner_hits`. | |
| 17 | +| `Query/PhrasePrefix` | `int $boost = 1` (inconsistent type). | `float $boost = 1.0`. | |
| 18 | +| `Options/GeoDistanceSort` | `ignore_unmapped` hard-coded to `true`. | Constructor arg `bool $ignoreUnmapped = true`. | |
| 19 | + |
| 20 | +### New query types |
| 21 | + |
| 22 | +- **Knn** — vector similarity (field, queryVector, k, numCandidates, similarity, filter, boost). |
| 23 | +- **SparseVector** — ELSER-style sparse vector query (inference_id+query or queryVector tokens). |
| 24 | +- **TextExpansion** — legacy ELSER form (model_id, model_text). |
| 25 | +- **Semantic** — queries a `semantic_text` field. |
| 26 | +- **RuleQuery** — Search Application query rules over an organic query. |
| 27 | +- **WeightedTokens** — token weights against a sparse_vector field. |
| 28 | + |
| 29 | +### Existing queries — new constructor arguments |
| 30 | + |
| 31 | +| Query | New args | |
| 32 | +| --- | --- | |
| 33 | +| `ElasticMatch` | `zero_terms_query`, `auto_generate_synonyms_phrase_query`, `lenient`, `prefix_length`, `max_expansions`, `fuzzy_transpositions`, `fuzzy_rewrite` | |
| 34 | +| `MultiMatch` | `tie_breaker`, `slop`, `prefix_length`, `max_expansions`, `lenient`, `zero_terms_query`, `auto_generate_synonyms_phrase_query`, `fuzzy_transpositions`, `fuzzy_rewrite` | |
| 35 | +| `MatchPhrase` | `zero_terms_query` | |
| 36 | +| `PhrasePrefix` | `analyzer`, `max_expansions`, `zero_terms_query` | |
| 37 | +| `MatchBoolPrefix` | `fuzziness`, `prefix_length`, `max_expansions`, `fuzzy_transpositions`, `fuzzy_rewrite` | |
| 38 | +| `QueryString` | `analyze_wildcard`, `auto_generate_synonyms_phrase_query`, `enable_position_increments`, `fuzziness`, `fuzzy_max_expansions`, `fuzzy_prefix_length`, `fuzzy_transpositions`, `lenient`, `max_determinized_states`, `minimum_should_match`, `quote_analyzer`, `phrase_slop`, `quote_field_suffix`, `rewrite`, `time_zone`, `type`, `tie_breaker` | |
| 39 | +| `SimpleQueryString` | `analyze_wildcard`, `auto_generate_synonyms_phrase_query`, `fuzzy_max_expansions`, `fuzzy_prefix_length`, `fuzzy_transpositions`, `lenient`, `minimum_should_match`, `quote_field_suffix` | |
| 40 | +| `CombinedFields` | `auto_generate_synonyms_phrase_query` | |
| 41 | +| `Term` | `case_insensitive` | |
| 42 | +| `Terms` | accepts `TermsLookup` for cross-document terms resolution | |
| 43 | +| `Range` | `gt`, `lt`, `format`, `relation` (new `Range\Relation` constants), `time_zone` | |
| 44 | +| `Exists` | `boost` | |
| 45 | +| `WildCard` | `case_insensitive`, `rewrite` | |
| 46 | +| `Prefix` | `rewrite` | |
| 47 | +| `Fuzzy` | `transpositions`, `rewrite` | |
| 48 | +| `Regexp` | `rewrite` | |
| 49 | +| `TermSet` | `boost` | |
| 50 | +| `HasChild` | `inner_hits` | |
| 51 | +| `HasParent` | `inner_hits` | |
| 52 | +| `Nested` | `score_mode`, `ignore_unmapped`, `inner_hits` | |
| 53 | +| `ParentId` | `boost` | |
| 54 | +| `GeoBoundingBox` | `validation_method`, `ignore_unmapped`, `boost` | |
| 55 | +| `GeoShape` | `indexed_shape` (new `IndexedShape` sub-object), `boost` | |
| 56 | +| `Shape` | `indexed_shape`, `boost` | |
| 57 | +| `MoreLikeThis` | `boost_terms`, `include`, `min_doc_freq`, `max_doc_freq`, `min_word_length`, `max_word_length`, `stop_words`, `analyzer`, `boost`, `fail_on_unsupported_field` | |
| 58 | +| `Percolate` | `documents` (multi-doc), `name`, `routing`, `preference`, `version` | |
| 59 | + |
| 60 | +### New sub-objects |
| 61 | + |
| 62 | +- `Query/TermsLookup` — `index`, `id`, `path`, `routing`. |
| 63 | +- `Query/Range/Relation` — constants: `INTERSECTS`, `CONTAINS`, `WITHIN`. |
| 64 | +- `Query/InnerHits` — `name`, `from`, `size`, `sort`, `_source`, `highlight`, `explain`, `script_fields`, `docvalue_fields`, `version`, `seq_no_primary_term`, `stored_fields`, `track_scores`. |
| 65 | +- `Query/IndexedShape` — `id`, `index`, `path`, `routing`. |
| 66 | +- `Script` (top-level) — reusable script value object (`source`, `lang`, `params`). |
| 67 | + |
| 68 | +### Aggregations — new types |
| 69 | + |
| 70 | +Bucket: `Filters` (named filters), `AutoDateHistogram`, `VariableWidthHistogram`, `CategorizeText` *(platinum license)*, `FrequentItemSets` *(platinum license)*, `IpPrefix`, `TimeSeries`. |
| 71 | + |
| 72 | +Metric: `TopMetrics`, `GeoLine` *(gold license)*, `TTest`, `Rate`, `MatrixStats`. |
| 73 | + |
| 74 | +Pipeline/sampler/ML: `RandomSampler`, `CumulativeCardinality`, `ExtendedStatsBucket`, `Inference`. |
| 75 | + |
| 76 | +### Aggregations — new constructor arguments |
| 77 | + |
| 78 | +| Agg | New args | |
| 79 | +| --- | --- | |
| 80 | +| `Min`/`Max`/`Avg`/`Sum`/`ValueCount`/`Stats` | `missing`, `script`, `format` | |
| 81 | +| `ExtendedStats` | `missing`, `script`, `format` (kept `sigma`) | |
| 82 | +| `Cardinality` | `script`, `missing`, `rehash` | |
| 83 | +| `MedianAbsoluteDeviation`/`StringStats` | `missing`, `script` | |
| 84 | +| `BoxPlot` | `missing`, `script`, `execution_hint` | |
| 85 | +| `Percentiles` | `tdigest`, `hdr`, `missing`, `script` | |
| 86 | +| `PercentileRanks` | `hdr`, `missing`, `script` | |
| 87 | +| `WeightedAvg` | **rewritten** — takes typed `WeightedAvgValue` for value/weight (each with `field`/`script`/`missing`), plus `format` | |
| 88 | +| `TopHits` | **rewritten** — `from`, `sort`, `_source`, `highlight`, `explain`, `script_fields`, `docvalue_fields`, `version`, `seq_no_primary_term`, `stored_fields`, `track_scores` | |
| 89 | +| `Term` | `min_doc_count`, `shard_size`, `shard_min_doc_count`, `show_term_doc_count_error`, `script`, `collect_mode`, `execution_hint`, `value_type`, `format`; `include`/`exclude` accept arrays | |
| 90 | +| `MultiTerms` | `order`, `min_doc_count`, `shard_size`, `shard_min_doc_count`, `collect_mode`, `format` | |
| 91 | +| `RareTerms` | `include`, `exclude`, `missing` | |
| 92 | +| `SignificantTerms` | `shard_size`, `shard_min_doc_count`, `execution_hint`, `background_filter`, `heuristic` (with `HEURISTIC_*` constants) | |
| 93 | +| `SignificantText` | `shard_size`, `shard_min_doc_count`, `min_doc_count`, `background_filter`, `source_fields` | |
| 94 | +| `Range` | `script`, `missing`, `format` | |
| 95 | +| `DateRange` | `script`, `missing` | |
| 96 | +| `Histogram` | `min_doc_count`, `extended_bounds` (new `Histogram\Bounds`), `hard_bounds`, `offset`, `order`, `script`, `missing`, `keyed`, `format` | |
| 97 | +| `DateHistogram` | `extended_bounds`, `hard_bounds`, `keyed`, `order`, `script`, `missing` | |
| 98 | +| `IpRange` | **rewritten** — new `IpRange\IpRangeValue` with `mask` (CIDR) support | |
| 99 | +| `Filter` | **rewritten** — accepts any `LeafQueryInterface` directly | |
| 100 | +| `Composite` | typed sources: `Composite\TermsSource`, `Composite\HistogramSource`, `Composite\DateHistogramSource`, `Composite\GeotileGridSource`, each with `order`/`missing_bucket` | |
| 101 | +| `AdjacencyMatrix` | `separator`, accepts `LeafQueryInterface` for filters | |
| 102 | +| `GeoDistance` (agg) | `keyed`, `script`, `missing` | |
| 103 | +| `GeoHashGrid`/`GeoTileGrid` | `bounds` | |
| 104 | +| `DiversifiedSampler` | `execution_hint`, `script` | |
| 105 | +| `Missing` | `script` | |
| 106 | + |
| 107 | +### Score functions |
| 108 | + |
| 109 | +- New `FunctionScore/ScoreFunction/Decay/Gauss`, `Linear`, `Exp` with shared `AbstractDecay` parent (`field`, `origin`, `scale`, `offset`, `decay`, `multi_value_mode`). |
| 110 | +- New `FunctionScore/ScoreFunction/ScriptScore` (function variant — distinct from the `Query/ScriptScore` leaf). |
| 111 | +- `FunctionScore` gained `boost`, `boost_mode` (with `BOOST_MODE_*` constants), `max_boost`, `min_score`. |
| 112 | + |
| 113 | +### Sort |
| 114 | + |
| 115 | +- `Sort` gains `mode`, `nested` (new `NestedSort`), `numeric_type`, `unmapped_type`, `format`. |
| 116 | +- New `Options/ScriptSort` — script-based sort. |
| 117 | +- New `Options/NestedSort` — path/filter/max_children for nested sorting (recursive). |
| 118 | + |
| 119 | +### Highlight — rewritten |
| 120 | + |
| 121 | +- `Highlight/HighlightField` — per-field config (type, number_of_fragments, fragment_size, all boundary_*, encoder, force_source, fragmenter, highlight_query, matched_fields, no_match_size, order, phrase_limit, require_field_match, tags_schema, pre_tags, post_tags). |
| 122 | +- `Highlight/HighlightFieldCollection` — typed collection. |
| 123 | +- `Highlight` accepts either `HighlightFieldCollection` or simple `array<string>` of field names (BC). Adds all global options. |
| 124 | + |
| 125 | +### Options — many new fields |
| 126 | + |
| 127 | +| Field | Type | |
| 128 | +| --- | --- | |
| 129 | +| `_source` | new `Options\Source` (includes/excludes, or `false`) | |
| 130 | +| `track_total_hits` | `bool\|int` | |
| 131 | +| `track_scores` | `bool` | |
| 132 | +| `explain` | `bool` | |
| 133 | +| `terminate_after` | `int` | |
| 134 | +| `timeout` | `string` | |
| 135 | +| `search_after` | `array` | |
| 136 | +| `pit` | new `Options\Pit` | |
| 137 | +| `stored_fields` | `array` | |
| 138 | +| `docvalue_fields` | `array` | |
| 139 | +| `fields` | `array` | |
| 140 | +| `script_fields` | `array` | |
| 141 | +| `runtime_mappings` | `array` | |
| 142 | +| `seq_no_primary_term` | `bool` | |
| 143 | +| `indices_boost` | `array` | |
| 144 | +| `collapse` | new `Options\Collapse` | |
| 145 | +| `rescore` | `array<Options\Rescore>` | |
| 146 | +| `suggesters` | `array<Suggest\SuggesterInterface>` | |
| 147 | +| `profile` | `bool` | |
| 148 | +| `stats` | `array<string>` | |
| 149 | +| `ext` | `array` | |
| 150 | + |
| 151 | +`ElasticQuery::toArray()` wires `collapse`, `rescore`, and `suggest` to the top-level request body. |
| 152 | + |
| 153 | +### Filter container — bool expansion |
| 154 | + |
| 155 | +`Filter/FilterCollection` previously exposed only `must()`. It now mirrors `Query/QueryCollection` with `must()`, `should()`, `mustNot()`, and `filter()` — the `bool` body emits all four arms. |
| 156 | + |
| 157 | +### Suggesters |
| 158 | + |
| 159 | +- `Options/Suggest/SuggesterInterface` |
| 160 | +- `Options/Suggest/TermSuggester` |
| 161 | +- `Options/Suggest/PhraseSuggester` |
| 162 | +- `Options/Suggest/CompletionSuggester` |
| 163 | + |
| 164 | +### Response mapper |
| 165 | + |
| 166 | +- `ResultMapper` now handles named buckets (string keys, e.g. from `Filters` agg) and composite-key buckets (array keys). |
| 167 | +- `Result/Aggregation/Bucket.from`/`to` accept `string` (e.g. for IP / date range buckets). |
| 168 | + |
| 169 | +### CI / tests |
| 170 | + |
| 171 | +- 218 tests, 3 skipped on basic license (geo_line, categorize_text). |
| 172 | +- ES 9.2.2 container in CI; `make tests` passes end-to-end against it. |
| 173 | +- The two pre-existing buggy tests for `GeoDistance` and `Nested` (which asserted invalid output) are now corrected and re-run as integration tests against ES. |
0 commit comments