Optionalprovider: IMetadataProviderOptional metadata provider to bind this instance (and all of its
metadata/view operations) to. Server-side callers servicing per-request connections
MUST pass their request-scoped provider (e.g. this.ProviderToUse from a BaseEntity
subclass) — relying on the global default silently uses the wrong connection in
multi-provider scenarios. Falls back to the global default provider when omitted.
Provider-aware metadata access: returns the explicit provider when one was supplied
(constructor or Provider setter), otherwise the global default. All metadata
operations in this class hierarchy go through this getter so a bound instance never
leaks onto the global provider.
Optional metadata provider override. Pass the provider to the constructor (preferred)
or set instance.Provider = providerToUse before invoking helper methods in
multi-provider contexts. Setting it rebinds the internal RunView instance so view
execution rides the same provider. Falls back to the global default when unset.
ProtectedapplyWrite the LLM verdict + run id onto a single match row's generated columns. The recommendation / confidence / reasoning are taken from THIS candidate's own verdict (judged independently), so a false-positive candidate reads NotDuplicate even when another candidate in the same set is a confident Merge. Falls back to the set-level summary only when the reasoner returned no per-candidate verdict for this record. The proposed survivor + field map stay set-level (they describe the merge of the set's true duplicates).
this candidate's record id (matches the input candidate RecordID)
ProtectedapplyCarry the set-level verdict + resolved survivor field map onto the result so the auto-merge step (AutoMergeAboveAbsolute) can consult the recommendation and apply the literal {FieldName, Value} overrides via RecordMergeRequest.FieldMap. The UI still reads the persisted per-row MJDuplicateRunDetailMatchEntity.LLMProposedFieldMap (the raw choices) and lets the reviewer override before a manual merge.
ProtectedBuildBuilds a SQL filter from composite keys. Values are sanitized to prevent SQL injection.
ProtectedBuildAssemble the reasoning input for a matched set: source description, candidate descriptions, and the differing-field deltas loaded for the whole set.
OptionalcontextUser: UserInfoProtectedbuildBuild a map of recordID → JSON metadata string from loaded BaseEntity records. Extracts the entity's name field and a few key display fields for rich UI rendering.
ProtectedCanReturns true when PageRecordsByEntityID can serve the given entity via
keyset (seek) pagination — i.e. the entity has a single-column PK on a comparable
type. Callers iterating large tables should consult this and pass AfterKey when true.
Check a single record for duplicates without requiring a list. Embeds the record and queries for matches directly.
OptionalOptions: DuplicateDetectionOptionsOptionalContextUser: UserInfoProtectedCreateCreate a new DuplicateRun record.
OptionallistID: stringProtectedCreateCreate match records for a single run detail, saving in parallel batches.
When a per-set DuplicateReasoningOutput is supplied, each match row gets THIS candidate's own verdict (recommendation / confidence / reasoning), while the set-level proposed survivor / field map / run id ride along on every row.
Optionalreasoning: DuplicateReasoningOutputProtectedCreateCreate DuplicateRunDetail records for a batch of record IDs.
OptionalmetadataMap: Map<string, string>ProtectedexecuteExecute one guarded auto-merge for an eligible candidate, applying the LLM's resolved field map when present, and stamp the match record on success.
ProtectedexecuteExecute a vector query — uses hybrid search with RRF when the provider supports it.
ProtectedFilterRemove matches whose target record no longer exists in the source entity.
Vector indexes (e.g. Pinecone) retain embeddings keyed by each record's composite key via a deterministic vector ID. When records are deleted — or re-seeded with new primary keys — their old vectors linger as "ghosts". A nearest-neighbour query then happily returns those ghosts, often the record's OWN former vector (identical name, ~0.98 score), which to a user looks exactly like a record matching itself. The per-record self-match filter can't catch these because the ghost carries the OLD id.
We verify every distinct match key for the batch against the live table in a single batched query and drop any that don't resolve. Single-column primary keys only — the same assumption the rest of this engine makes.
Mutates queryResults in place.
ProtectedFilterProtectedFindTry to find an existing DuplicateRun for a given ListID. Returns null if none found.
ProtectedGenerateGenerate human-readable template text for each record using the entity document template.
Loads the template from TemplateEngineServer and renders it via Nunjucks, matching the same approach used by the vectorization pipeline.
ProtectedGetOptionalid: stringRun duplicate detection for records identified by ListID, ViewID, ExtraFilter, or all records in the entity (vector-first approach).
Batching strategy:
Resume support:
Cancellation:
true, processing stops and the run can be resumed later.The detection request specifying entity, document, list/view/filter, and options
OptionalcontextUser: UserInfoThe user context for entity operations and security
A response containing all potential duplicate results and an overall status
ProtectedGetRead the maxConcurrentRequests from the VectorDatabase entity's Configuration column, falling back to DEFAULT_QUERY_CONCURRENCY if not set.
ProtectedGetOptionalrecordIDs: CompositeKey[]ProtectedGetOptionalid: stringProtectedInitializeInitializes embedding model, vector DB, and index name providers. Called once per detection run rather than per-record. Uses AIEngine and KnowledgeHubMetadataEngine caches to avoid redundant database queries.
ProtectedIsDecide whether a single candidate is eligible for automatic merge.
Back-compat path (EnableLLMReasoning = false): eligible iff the vector score meets the
absolute threshold — byte-for-byte the original behavior. AutomationLevel is ignored.
Reasoning path (EnableLLMReasoning = true): AutomationLevel governs.
ProtectedIsGate predicate: reasoning enabled + non-empty set + top score clears the threshold. A null ReasoningThreshold means "no gate" — reasoning runs for any non-empty set.
ProtectedisTrue when two composite keys identify the same record. Compares the normalized (trimmed, lower-cased) URL-segment representation, so it is robust to UUID-casing differences across platforms (SQL Server upper- vs PostgreSQL lower-case) and to a bare-PK vs concatenated key shape.
ProtectedloadPopulate a candidate's composite key from a vector match's RecordID, tolerant of the two formats different vector providers emit:
MJ: Entity Record Documents.RecordID as the raw PK value. For
this form we rebuild the key using the source record's PK field name(s).Getting this wrong leaves KeyValuePairs empty (the bare value has no field delimiter, so
LoadFromConcatenatedString no-ops), which silently breaks self-match filtering, candidate
field-delta loading (the reasoner sees an empty candidate), and MatchRecordID persistence.
OptionalsourceKey: CompositeKeyProtectedLoadProtectedLoadReturn the set of primary key values (normalized for case-insensitive UUID comparison) that actually exist in the entity, for the given candidate IDs. Batches the IN-list to stay within reasonable query sizes.
ProtectedLoadProtectedLoadLoad record IDs directly from the entity, optionally filtered. Uses Fields: ['ID'] and ResultType: 'simple' for efficiency.
OptionalextraFilter: stringProtectedLoadLoad record IDs from a list's detail records.
ProtectedLoadLoad record IDs by running a saved view.
ProtectedLoadLoad the IDs of records to check, using the appropriate strategy based on the request. Returns an array of primary key value strings.
ProtectedLoadLoad full entity objects for a batch of composite keys.
ProtectedLoadLoad records from an entity that are members of the specified list. Kept for backward compatibility.
ProtectedloadLoad the template entity from TemplateEngineServer for the given entity document.
ProtectedPageParse raw vector DB matches into a PotentialDuplicateResult.
OptionalsourceKey: CompositeKeyProtectedpersistLocate the matching run-detail row, create the per-candidate match records (carrying the set's LLM verdict when present), mark the detail complete, and stamp the created match-record IDs back onto the result (kept in lockstep with Duplicates for auto-merge).
Optionalreasoning: DuplicateReasoningOutputProtectedPersistPersist match results and update run detail records.
When LLM reasoning is enabled on the entity document AND a matched set clears the reasoning gate (top vector score >= ReasoningThreshold), reasoning runs ONCE for the whole set; the verdict is written onto every match row's LLM* columns and carried on the result for the auto-merge step. When reasoning is disabled the loop is unchanged.
OptionalcontextUser: UserInfoProtectedProcessAutomatically merge records that meet the absolute match threshold.
ProtectedQueryQuery the vector DB for duplicates of each record, with concurrency control.
Creates one async task per record, then executes them via RunWithConcurrency with the specified concurrency limit. Each task embeds and queries a single record against the vector index.
Supports hybrid search (vector + keyword) with RRF fusion when the vector DB provider
supports it (checked via SupportsHybridSearch).
Post-query, results are:
The source records to find duplicates for
Pre-computed embedding vectors, one per record (same index order)
Rendered template texts for hybrid keyword search
The entity document providing thresholds and configuration
Number of nearest neighbors to retrieve per record
Detection options including threshold overrides
Max parallel vector queries
One RecordQueryResult per input record
ProtectedreasoningExtract a display label from a candidate's vector metadata snapshot, if present.
Optionalmetadata: Record<string, string>ProtectedrecordCase-insensitive, trimmed equality of two record-id (URL-segment) strings.
ProtectedResolveResolve an existing DuplicateRun or create a new one. Supports both list-based and list-free operation.
ProtectedresolveResolve the reasoner's per-field survivor choices ({FieldName, SourceRecordID}) into
literal {FieldName, Value} entries for RecordMergeRequest.FieldMap, by reading
the chosen record's value out of the matched-set deltas. Choices whose field or record
can't be located in the deltas are skipped (the survivor's existing value then stands),
so a partial/garbled choice can never inject an undefined override into the merge.
ProtectedResolveResolve the reasoning provider for this entity document's ReasoningMode via the ClassFactory. Returns null when ClassFactory falls back to the abstract base (no implementation registered for the mode) so the caller can skip gracefully.
ProtectedresolveResolve a record's display label: prefer the name loaded from the record (via the delta builder), then a secondary fallback (e.g. a candidate's vector-metadata name), then the raw record-id key as a last resort. Guards against the loaded label itself being the record key (the engine's own fallback) so we still try the secondary source.
ProtectedRunRun LLM reasoning for one source record's matched set, ONCE, only when:
Returns undefined in every other case — including when reasoning is disabled — so
the surrounding persist/merge path stays byte-for-byte identical to the vector-only
behavior. A failed reasoning call returns an output with Success = false (never throws),
so one bad set never aborts the run.
OptionalcontextUser: UserInfoProtectedRunProtectedSaveSaving an Entity in any vector related package needs the CurrentUser property to be set on the entity So this is a simple wrapper to set it before saving
ProtectedsuppressDrop inverse duplicates in place: if A→B was already persisted this run, skip B→A.
ProtectedValidateValidates that an entity document exists and is usable. Uses the KnowledgeHubMetadataEngine cache for instant lookups without database queries.
ProtectedvalidateNull out a hallucinated survivor: the reasoner can return a SurvivorRecordID that isn't any record in the set (a made-up GUID, or a candidate that was filtered out). Persisting it would carry a phantom id to the UI and auto-merge. Validate against the set's record ids (source + candidates) and drop it if it doesn't match.
ProtectedVectorizeRun vectorization for the entity document's records.
Modernized duplicate record detection engine.
Supports: