Skip to content

refactor: reflection-based search mapping + location geopoint#2659

Open
dschmidt wants to merge 7 commits into
opencloud-eu:mainfrom
dschmidt:refactor/search-mapping
Open

refactor: reflection-based search mapping + location geopoint#2659
dschmidt wants to merge 7 commits into
opencloud-eu:mainfrom
dschmidt:refactor/search-mapping

Conversation

@dschmidt

@dschmidt dschmidt commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Merges the bleve and OpenSearch index mappings into one reflection-based package (services/search/pkg/mapping) driven by the search.Resource struct + a small overrides map. Pulls services/graph's duplicated reflection walker onto the same helpers and, as a showcase of what the refactor enables, turns on location_geopoint indexing for spatial queries on both backends.

Adding a new facet (motionPhoto, ...) is now roughly: one struct field on content.Document, one line in service.go, one line in graph, one line per backend hit converter, plus whatever tika extraction logic the facet actually needs. Everything else falls out of reflection.

Behavior changes (deliberate)

Existing indexes keep their stored mapping; the new shape only applies to newly-created indexes.

  • OpenSearch Tags / Favorites: dynamic keywordtext + lowercaseKeyword (unified with bleve; the analyzer is registered in the new index settings).
  • OpenSearch facet sub-strings (audio.*, photo.*, image.*): dynamic text + keyword multi-field → keyword-only. The tokenized path was never reachable from KQL anyway (no dot-syntax + pre-fix(search): preserve value case for non-lowercased bleve fields #2633 lowercasing), so no working query regresses; aggregations now produce correct case-preserving buckets on both backends.
  • location: the libregraph {longitude, latitude, altitude} object is preserved at the location key on both backends (numeric sub-field queries like location.latitude:>49 keep working). A sibling location_geopoint is added for geo-distance / bounding-box / polygon queries.
  • graph facet parsing: fail-soft per field. A malformed value drops only that field; the rest of the facet still populates.
  • Audio facet is now shown whenever libre.graph.audio.* metadata is present, regardless of MIME type (the old audio/ guard is dropped) - on read and write consistently.
  • OpenSearch Name/Tags tokenization now matches bleve (single lowercase token, was word-tokenized). No impact on the product: web always searches wildcarded (name:"*term*", see web useSearch.ts), which matches regardless of tokenization; bleve has always been single-token. Only affects non-web clients sending a bare name:report and expecting a substring match.
  • Mtime is typed as a date on both backends, so mtime:>... ranges are chronological (was a keyword field / lexicographic compare on OpenSearch).
  • Resource.Hidden now survives Move/Delete/Restore on bleve. The old hand-rolled deserializer never read Hidden, so those ops silently reset it to false; the reflection deserializer reads every field, preserving it. Latent-bug fix.

Upgrade note: the OpenSearch mapping (index resource_v2) now lists all properties explicitly, so it differs from any existing resource_v2 index. On startup Apply returns ErrManualActionRequired with a clear message; operators upgrading in place must drop and reindex resource_v2. Fresh installs and the bleve backend are unaffected.

@dschmidt
dschmidt force-pushed the refactor/search-mapping branch from ae6414c to 531fd21 Compare April 22, 2026 23:33
@dschmidt dschmidt changed the title refactor(search): build index mappings via reflection + generic helpers refactor: reflection-based search mapping + location geopoint Apr 23, 2026
dschmidt added a commit to dschmidt/opencloud that referenced this pull request Apr 23, 2026
Propose a single central Go-struct + overrides map as the source of
truth for the search index layout across bleve and OpenSearch — the
same definition drives the per-backend index mapping, the write-time
adapter, the hit-decoding path, and the KQL compiler's case-folding
rules, so the two backends cannot drift silently again.

Also records the end-to-end case-handling principle for facet data
(indexed as case-preserving keywords so aggregation buckets return
correct display values), the sibling-field pattern for geopoint on
Location, and the rationale for replacing the two backends' implicit
defaults with an explicit, backend-agnostic contract.

PR opencloud-eu#2659 is a proof-of-concept implementation the proposal emerged
from; scope and APIs there will be revisited once this ADR lands.
dschmidt added a commit to dschmidt/opencloud that referenced this pull request Apr 23, 2026
Propose a single central Go-struct + overrides map as the source of
truth for the search index layout across bleve and OpenSearch — the
same definition drives the per-backend index mapping, the write-time
adapter, the hit-decoding path, and the KQL compiler's case-folding
rules, so the two backends cannot drift silently again.

Also records the end-to-end case-handling principle for facet data
(indexed as case-preserving keywords so aggregation buckets return
correct display values), the sibling-field pattern for geopoint on
Location, and the rationale for replacing the two backends' implicit
defaults with an explicit, backend-agnostic contract.

PR opencloud-eu#2659 is a proof-of-concept implementation the proposal emerged
from; scope and APIs there will be revisited once this ADR lands.
dschmidt added a commit to dschmidt/opencloud that referenced this pull request Apr 23, 2026
Propose a single central Go-struct + overrides map as the source of
truth for the search index layout across bleve and OpenSearch — the
same definition drives the per-backend index mapping, the write-time
adapter, the hit-decoding path, and the KQL compiler's case-folding
rules, so the two backends cannot drift silently again.

Also records the end-to-end case-handling principle for facet data
(indexed as case-preserving keywords so aggregation buckets return
correct display values), the sibling-field pattern for geopoint on
Location, and the rationale for replacing the two backends' implicit
defaults with an explicit, backend-agnostic contract.

PR opencloud-eu#2659 is a proof-of-concept implementation the proposal emerged
from; scope and APIs there will be revisited once this ADR lands.
dschmidt added a commit to dschmidt/opencloud that referenced this pull request Apr 23, 2026
Propose a single central Go-struct + overrides map as the source of
truth for the search index layout across bleve and OpenSearch — the
same definition drives the per-backend index mapping, the write-time
adapter, the hit-decoding path, and the KQL compiler's case-folding
rules, so the two backends cannot drift silently again.

Also records the end-to-end case-handling principle for facet data
(indexed as case-preserving keywords so aggregation buckets return
correct display values), the sibling-field pattern for geopoint on
Location, and the rationale for replacing the two backends' implicit
defaults with an explicit, backend-agnostic contract.

PR opencloud-eu#2659 is a proof-of-concept implementation the proposal emerged
from; scope and APIs there will be revisited once this ADR lands.
dschmidt added a commit to dschmidt/opencloud that referenced this pull request Apr 27, 2026
Propose a single central Go-struct + overrides map as the source of
truth for the search index layout across bleve and OpenSearch — the
same definition drives the per-backend index mapping, the write-time
adapter, the hit-decoding path, and the KQL compiler's case-folding
rules, so the two backends cannot drift silently again.

Also records the end-to-end case-handling principle for facet data
(indexed as case-preserving keywords so aggregation buckets return
correct display values), the sibling-field pattern for geopoint on
Location, and the rationale for replacing the two backends' implicit
defaults with an explicit, backend-agnostic contract.

PR opencloud-eu#2659 is a proof-of-concept implementation the proposal emerged
from; scope and APIs there will be revisited once this ADR lands.
@dragonchaser

Copy link
Copy Markdown
Member

Why the extensive usage of reflections?

@dschmidt

dschmidt commented Apr 30, 2026

Copy link
Copy Markdown
Contributor Author

Short version:
It's there so the search.Resource struct (plus a small overrides map) becomes the single source of truth for everything schema-shaped: the bleve DocumentMapping, the OpenSearch properties JSON, and the hit→struct deserialization on read. One struct, two backends, no parallel schemas to keep in sync.

Keeping all the mappings in sync for the existing facets and having to do a lot of copy paste for adding new facets, is tedious and error prone.
The new code is not completely trivial to read, I agree, but it's basically write once and probably/hopefully never touch again. Basically it pulls together different pieces of reflection usage in a central location and concentrates it in the mapping package, so actually there's less reflection stuff spread around the code base.

Long version

  1. No drift between bleve and OpenSearch. right now there are two hand-maintained mapping definitions plus a third hand-maintained "read fields back out of hit.Fields" path. They have already drifted (e.g. the OpenSearch dynamic-keyword vs bleve text+lowercaseKeyword mismatch the PR description calls out). With reflection both backends walk the same fields with the same json-tag names via walkFields (infer.go), so they can't disagree by accident.

  2. Kills a duplicated walker. services/graph had its own reflection walker doing essentially the same thing; this PR collapses it onto the shared helpers in mapping/infer.go (walkFields, resolveField, inferType, structType).

  3. Adding a facet is now ~5 lines. As the PR description says: one field on content.Document, one line in service.go, one in graph, one per backend hit converter. No new mapping JSON, no new "read this key out of bleve" branch — Deserialize[T] (deserialize.go) handles it from the json tags. That's the whole point of the refactor and it's why location_geopoint could be added as a near-trivial showcase.

  4. json tags are already the contract. Field names on the wire are already driven by struct tags for marshalling, so reusing them via reflection for index field names just removes a second, hand-typed copy of the same names.

  5. Cost is bounded. Reflection runs once at index-mapping build time (startup) and once per hit on read. It's not on a hot inner loop and it's not in the query path — bleve/OpenSearch do the actual searching natively against the materialized mapping.

The alternative would be either codegen (more machinery for the same outcome) or keeping the three parallel hand-written schemas (which is what already doesnt work right now and what motivated the refactor).

@dschmidt

Copy link
Copy Markdown
Contributor Author

By the way looking at the line counts is a bit misleading. It's not 1.8k lines plus for a simple refactor.

Yes, it's a bit more code for staying consistent and making further additions easier - but a lot of the new lines are also for tests we simply didn't have before and it also adds the geopoint feature (we can of course discuss to split it out of this PR if you prefer, it's basically a demonstrator for the concept)

@dschmidt
dschmidt deleted the branch opencloud-eu:main May 5, 2026 10:35
@dschmidt dschmidt closed this May 5, 2026
@dschmidt dschmidt reopened this May 5, 2026
@dschmidt
dschmidt changed the base branch from fix/search-preserve-value-case to main May 5, 2026 10:56
@dschmidt
dschmidt force-pushed the refactor/search-mapping branch from 87589de to a25f04a Compare May 5, 2026 12:09
@sonarqubecloud

sonarqubecloud Bot commented May 5, 2026

Copy link
Copy Markdown

@codacy-production

codacy-production Bot commented May 12, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 229 complexity · -86 duplication

Metric Results
Complexity 229
Duplication -86

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@dschmidt
dschmidt marked this pull request as ready for review May 12, 2026 07:20
@dschmidt
dschmidt marked this pull request as draft May 12, 2026 07:21

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

The PR introduces a unified reflection-driven mapping system and spatial query support via 'location_geopoint'. While the architecture aligns with the goal of consolidating Bleve and OpenSearch logic, the implementation introduces critical risks. Specifically, the reflection walkers in the deserialization packages lack pointer handling for embedded structs, which will lead to runtime panics. \n\nFurthermore, although the codebase remains 'up to standards' according to Codacy, there are substantial increases in cyclomatic complexity in 'driveitems.go' (+114) and 'service.go' (+129). These core files should be monitored for maintainability. The change to drop entire facets upon encountering malformed metadata is a deliberate shift to ensure data integrity, though it differs from previous 'fail-soft' patterns. Note that existing indexes must be recreated to adopt the new 'location_geopoint' and analyzer changes.

About this PR

  • This refactor introduces behavioral changes in indexing (e.g., custom analyzers and geopoint fields). Existing indexes will not be automatically migrated; users must recreate their indexes to benefit from these changes.
  • Jira ticket key and description are missing from the PR metadata.

Test suggestions

  • Geo-distance query on location_geopoint field in Bleve\n- [x] Numeric range queries on longitude, latitude, and altitude sub-fields\n- [x] Inference of mapping types (keyword, numeric, boolean, datetime) from Go struct fields\n- [x] Application of field mapping overrides (e.g., custom analyzers, explicit types)\n- [x] Deserialization of flat map[string]any (Bleve results) into nested Go structs\n- [x] Deserialization of flat map[string]string (CS3 metadata) into typed facets with error bubbling\n- [x] Verification that PrepareForIndex correctly adds sibling geopoint fields for OpenSearch/Bleve

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread services/search/pkg/mapping/deserialize_string.go Outdated
Comment thread services/search/pkg/mapping/deserialize.go Outdated
Comment thread services/search/pkg/mapping/deserialize_string.go Outdated
@dschmidt
dschmidt force-pushed the refactor/search-mapping branch from f03a98e to 9e1e56b Compare May 12, 2026 11:00
@dschmidt

Copy link
Copy Markdown
Contributor Author

relevant #2715

@dschmidt
dschmidt force-pushed the refactor/search-mapping branch 2 times, most recently from 370ac9d to 4e8381a Compare July 1, 2026 16:34
@dschmidt
dschmidt requested a review from Copilot July 1, 2026 16:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR consolidates Bleve and OpenSearch index mapping generation into a shared, reflection-driven services/search/pkg/mapping package based on search.Resource + an overrides map, and extends indexing to support spatial queries by adding a *_geopoint sibling field (while preserving the original libregraph {longitude, latitude, altitude} object).

Changes:

  • Introduce services/search/pkg/mapping to infer field types via reflection, validate override keys, and build Bleve/OpenSearch mappings consistently.
  • Switch Bleve/OpenSearch indexing and Bleve hit decoding to use the shared mapping + (de)serialization helpers.
  • Enable geo queries by adding location_geopoint (and related serialization support) while keeping location.* numeric subfields intact.

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
services/search/pkg/search/service.go Refactors facet metadata flattening into a generic helper.
services/search/pkg/search/search.go Adds JSON tags to Resource and introduces shared field override map for mapping generation.
services/search/pkg/query/bleve/compiler.go Derives lowercase-query fields from mapping overrides to keep query tokenization aligned with index analyzers.
services/search/pkg/opensearch/internal/indexes/resource_v2.json Removes static OpenSearch mapping JSON (replaced by generator).
services/search/pkg/opensearch/internal/indexes/resource_v1.json Removes legacy static OpenSearch mapping JSON.
services/search/pkg/opensearch/internal/convert/opensearch.go Simplifies facet conversion from indexed source to protobuf via a generic helper.
services/search/pkg/opensearch/index.go Replaces embedded JSON templates with generated OpenSearch index mapping built from shared reflection mapping + overrides.
services/search/pkg/opensearch/batch.go Uses mapping.PrepareForIndex to keep document shape in sync with generated mappings (incl. geopoint sibling).
services/search/pkg/mapping/opts.go Defines mapping type constants and override options.
services/search/pkg/mapping/infer.go Adds reflection-based type inference and shared field walking helpers (incl. embedded flattening).
services/search/pkg/mapping/infer_test.go Tests type inference and field walking behavior.
services/search/pkg/mapping/validate.go Adds override-key validation against reflected JSON-tag field names (incl. dotted paths).
services/search/pkg/mapping/validate_test.go Tests override validation behavior.
services/search/pkg/mapping/bleve.go Builds Bleve document mappings via reflection with support for object + geopoint sibling mapping.
services/search/pkg/mapping/bleve_test.go Tests Bleve mapping inference/overrides and geopoint sibling mapping.
services/search/pkg/mapping/opensearch.go Builds OpenSearch mapping properties via reflection with support for geopoint sibling mapping.
services/search/pkg/mapping/opensearch_test.go Tests OpenSearch mapping inference/overrides and geopoint sibling mapping.
services/search/pkg/mapping/geo.go Adds serialization support to splice {lat, lon} geopoint siblings into indexed documents.
services/search/pkg/mapping/geo_test.go Tests geopoint sibling insertion behavior (top-level and dotted nested paths).
services/search/pkg/mapping/serialize.go Adds a shared “prepare document for indexing” step (struct→map + geopoint sibling injection).
services/search/pkg/mapping/serialize_test.go Tests serialization behavior for embedded flattening and omitempty handling.
services/search/pkg/mapping/deserialize.go Adds reflection-based fail-soft deserialization from Bleve hit fields into typed structs.
services/search/pkg/mapping/deserialize_test.go Tests fail-soft typed deserialization from Bleve hit fields.
services/search/pkg/mapping/deserialize_string.go Adds reflection-based fail-soft deserialization from map[string]string (CS3 arbitrary metadata) into typed structs.
services/search/pkg/mapping/deserialize_string_test.go Tests fail-soft string-map deserialization semantics.
services/search/pkg/content/content.go Adds JSON tags to content.Document fields to align serialization and mapping generation.
services/search/pkg/bleve/index.go Builds Bleve index mapping via the shared mapping package (reflection + overrides).
services/search/pkg/bleve/geo_verify_test.go Adds end-to-end verification for location subfields + geopoint geo-distance queries in Bleve.
services/search/pkg/bleve/bleve.go Switches resource reconstruction from Bleve hits to mapping.Deserialize.
services/search/pkg/bleve/batch.go Uses mapping.PrepareForIndex before indexing into Bleve batches (incl. geopoint siblings).
services/search/pkg/bleve/backend.go Uses mapping.DeserializeAt to populate facets from Bleve hit fields.
services/graph/pkg/service/v0/driveitems.go Switches facet decoding from CS3 arbitrary metadata to mapping.DeserializeStringMap.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/search/pkg/search/service.go Outdated
Comment thread services/search/pkg/mapping/deserialize.go
Comment thread services/search/pkg/mapping/deserialize_string.go Outdated
Comment thread services/graph/pkg/service/v0/driveitems.go Outdated
@dschmidt
dschmidt force-pushed the refactor/search-mapping branch 2 times, most recently from aa9a500 to 1c13f04 Compare July 1, 2026 17:12
@dschmidt
dschmidt force-pushed the refactor/search-mapping branch from 1c13f04 to 2accce3 Compare July 1, 2026 17:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 36 changed files in this pull request and generated 2 comments.

Comment thread services/search/pkg/mapping/validate.go Outdated
Comment thread services/search/pkg/mapping/embedded_test.go Outdated
@dschmidt
dschmidt force-pushed the refactor/search-mapping branch 6 times, most recently from 6947cf7 to 98f896b Compare July 2, 2026 13:46
dschmidt added 2 commits July 2, 2026 16:04
Build the bleve and OpenSearch index mappings from the Go struct via
reflection (json tags + per-field overrides) instead of hand-rolled
mappings and hit deserializers. New mapping package: BleveBuildMapping,
OpenSearchBuildMapping, Deserialize[T], PrepareForIndex; field decoding is
fail-soft. Mtime is typed as a date so mtime ranges are chronological on
both backends. Route CS3 facet parsing through mapping.DeserializeStringMap.

The any-valued (bleve hit) and string-valued (CS3 metadata) deserializers
share one generic fillStruct walker with a per-value setLeaf callback.
Add a TypeGeopoint field type. The libregraph Location facet is kept as an
object (retrieval / numeric queries) and a sibling <name>_geopoint field
carries the {lat,lon} form for geo-distance / bbox / polygon queries,
uniform across bleve and OpenSearch via the shared mapping. PrepareForIndex
splices the sibling in at write time.
@dschmidt
dschmidt force-pushed the refactor/search-mapping branch from 98f896b to 0046873 Compare July 2, 2026 14:06

@aduffeck aduffeck left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with the change in general, unifiying and streamlining the index creating and the mapping should be worth the effort and the partly heavy code. I would like to alo hear @fschade 's opinion however before moving this forward (he should be back next week).

Comment thread services/search/pkg/bleve/geo_verify_test.go Outdated
IndexManagerLatest = IndexIndexManagerResourceV2
IndexIndexManagerResourceV1 IndexManager = "resource_v1.json"
IndexIndexManagerResourceV2 IndexManager = "resource_v2.json"
IndexIndexManagerResourceV2 IndexManager = "resource_v2"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requiring another re-indexing of the whole tree right after the stable 7.2 release is unfortunate, but I understand there's no way to do an in-place migration of the existing index with the field changes we do.
But maybe this is the time to spend some time on making index upgrades less intrusive. It should be possible to add a read alias on the v2 index while building a v3 index and then flip the alias over to the new index once it's done. I have no idea how easy and robust that is to do programmatically keeping all possible deployment scenarios in mind.
At the very least we should use "v3" as the index though, if the full-blown automation isn't possible, so that the new index could be built up in a pre-deployment hook in kubernetes, for example.

@dschmidt dschmidt Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but requiring it right before the release would have been risky on the other hand - there's never a perfect timing for this kind of change :)

We'll discuss soft upgrade options with @fschade next week :)

Comment thread services/search/pkg/search/search.go Outdated
@aduffeck

aduffeck commented Jul 3, 2026

Copy link
Copy Markdown
Member

Oh, one more remark: it would be great if you would just add commits instead of force-pushing refinements, that makes it easier to figure out what changed after starting the review.

@dschmidt

dschmidt commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Yes, will do from now on - thought noone had started reviewing and wanted to make a clean start from the consolidated state. Will only add commits on top from now on!

dschmidt added 3 commits July 5, 2026 16:56
Mtime is now a date field; the fixture's Go-format string fails
OpenSearch date parsing.
The package's engine suite is ginkgo; these new tests were plain.
New package, so use the repo's standard test framework.
@dschmidt
dschmidt marked this pull request as ready for review July 5, 2026 15:17
@dschmidt
dschmidt requested a review from aduffeck July 5, 2026 15:17
The Mtime field is mapped as an OpenSearch `date`, which rejects an
empty value with `mapper_parsing_exception: cannot parse empty date`.
The folder and root fixtures had no Mtime, so serializing them to
`"Mtime": ""` made TestEngine_Purge/purge_resource_trees fail when the
document was indexed. Give both a valid RFC3339 Mtime, matching the
file fixture.
@fschade

fschade commented Jul 8, 2026

Copy link
Copy Markdown
Member

Okay, the PR is huge, but I can understand that it’s hard to logically split up such a massive topic.

The only thing bothering me right now is the schema update issue; I can't think of a clean way to handle that independently of the deployment, and a "batteries-included" approach definitely isn't the right approach here.

I think we just need to document clearly what needs to be done for schema updates (and we’d face that problem regardless of this PR, other ideas?).

From my side: thumbs up.

@fschade

fschade commented Jul 8, 2026

Copy link
Copy Markdown
Member

follow up documentation issue: #3092
please give it a read and let me know if it covers the pain points!

@fschade fschade left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 follow up issue created (docs): FYI: @Svanvith

aduffeck
aduffeck previously approved these changes Jul 8, 2026
@aduffeck
aduffeck dismissed their stale review July 8, 2026 11:56

The code itself looks fine to me now, but we need to come up with a proper upgrade/migration path which will handle the incompatible index before merging this. @fschade is looking into that right now.

@dschmidt
dschmidt force-pushed the refactor/search-mapping branch from 4a43497 to 8c6a3b6 Compare July 15, 2026 23:55
@dschmidt
dschmidt force-pushed the refactor/search-mapping branch from 8c6a3b6 to 429a9c0 Compare July 16, 2026 00:23
Name string `json:"Name"`
Content string `json:"Content"`
Size uint64 `json:"Size"`
Mtime *time.Time `json:"Mtime"`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mtime was the only Go string mapped as a date. Basic.Extract leaves it unset when the resource info carries no mtime (TSToTime(nil) is the zero time and it deliberately does not index a made up 0001-01-01), which serialised to "Mtime": "" -- and an empty string is not a date, so OpenSearch rejected the whole document. The bulk API reports that per item, so Batch.Push returned nil and the file silently vanished from the index (that swallowing is #3142).

Typing it as *time.Time fixes the cause rather than the symptom: inferType picks the date mapping up by itself, so the TypeDatetime override goes, and convert gets its time.Time without the time.Parse detour. Net -4 lines of production code.

Reproduced against a real cluster: before this, upsert without mtime indexed 1 of 2 documents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants