opusdev/vector-similarity-api
1
1import type { Document } from './bson';2import { MongoInvalidArgumentError } from './error';3import type { TagSet } from './sdam/server_description';4import type { ClientSession } from './sessions';5 6/** @public */7export type ReadPreferenceLike = ReadPreference | ReadPreferenceMode;8 9/** @public */10export const ReadPreferenceMode = Object.freeze({11 primary: 'primary',12 primaryPreferred: 'primaryPreferred',13 secondary: 'secondary',14 secondaryPreferred: 'secondaryPreferred',15 nearest: 'nearest'16} as const);17 18/** @public */19export type ReadPreferenceMode = (typeof ReadPreferenceMode)[keyof typeof ReadPreferenceMode];20 21/** @public */22export interface HedgeOptions {23 /** Explicitly enable or disable hedged reads. */24 enabled?: boolean;25}26 27/** @public */28export interface ReadPreferenceOptions {29 /** Max secondary read staleness in seconds, Minimum value is 90 seconds.*/30 maxStalenessSeconds?: number;31 /** Server mode in which the same query is dispatched in parallel to multiple replica set members. */32 hedge?: HedgeOptions;33}34 35/** @public */36export interface ReadPreferenceLikeOptions extends ReadPreferenceOptions {37 readPreference?:38 | ReadPreferenceLike39 | {40 mode?: ReadPreferenceMode;41 preference?: ReadPreferenceMode;42 tags?: TagSet[];43 maxStalenessSeconds?: number;44 };45}46 47/** @public */48export interface ReadPreferenceFromOptions extends ReadPreferenceLikeOptions {49 session?: ClientSession;50 readPreferenceTags?: TagSet[];51 hedge?: HedgeOptions;52}53 54/**55 * The **ReadPreference** class is a class that represents a MongoDB ReadPreference and is56 * used to construct connections.57 * @public58 *59 * @see https://www.mongodb.com/docs/manual/core/read-preference/60 */61export class ReadPreference {62 mode: ReadPreferenceMode;63 tags?: TagSet[];64 hedge?: HedgeOptions;65 maxStalenessSeconds?: number;66 /**67 * @deprecated This will be removed as dead code in the next major version.68 */69 minWireVersion?: number;70 71 public static PRIMARY = ReadPreferenceMode.primary;72 public static PRIMARY_PREFERRED = ReadPreferenceMode.primaryPreferred;73 public static SECONDARY = ReadPreferenceMode.secondary;74 public static SECONDARY_PREFERRED = ReadPreferenceMode.secondaryPreferred;75 public static NEAREST = ReadPreferenceMode.nearest;76 77 public static primary = new ReadPreference(ReadPreferenceMode.primary);78 public static primaryPreferred = new ReadPreference(ReadPreferenceMode.primaryPreferred);79 public static secondary = new ReadPreference(ReadPreferenceMode.secondary);80 public static secondaryPreferred = new ReadPreference(ReadPreferenceMode.secondaryPreferred);81 public static nearest = new ReadPreference(ReadPreferenceMode.nearest);82 83 /**84 * @param mode - A string describing the read preference mode (primary|primaryPreferred|secondary|secondaryPreferred|nearest)85 * @param tags - A tag set used to target reads to members with the specified tag(s). tagSet is not available if using read preference mode primary.86 * @param options - Additional read preference options87 */88 constructor(mode: ReadPreferenceMode, tags?: TagSet[], options?: ReadPreferenceOptions) {89 if (!ReadPreference.isValid(mode)) {90 throw new MongoInvalidArgumentError(`Invalid read preference mode ${JSON.stringify(mode)}`);91 }92 if (options == null && typeof tags === 'object' && !Array.isArray(tags)) {93 options = tags;94 tags = undefined;95 } else if (tags && !Array.isArray(tags)) {96 throw new MongoInvalidArgumentError('ReadPreference tags must be an array');97 }98 99 this.mode = mode;100 this.tags = tags;101 this.hedge = options?.hedge;102 this.maxStalenessSeconds = undefined;103 this.minWireVersion = undefined;104 105 options = options ?? {};106 if (options.maxStalenessSeconds != null) {107 if (options.maxStalenessSeconds <= 0) {108 throw new MongoInvalidArgumentError('maxStalenessSeconds must be a positive integer');109 }110 111 this.maxStalenessSeconds = options.maxStalenessSeconds;112 113 // NOTE: The minimum required wire version is 5 for this read preference. If the existing114 // topology has a lower value then a MongoError will be thrown during server selection.115 this.minWireVersion = 5;116 }117 118 if (this.mode === ReadPreference.PRIMARY) {119 if (this.tags && Array.isArray(this.tags) && this.tags.length > 0) {120 throw new MongoInvalidArgumentError('Primary read preference cannot be combined with tags');121 }122 123 if (this.maxStalenessSeconds) {124 throw new MongoInvalidArgumentError(125 'Primary read preference cannot be combined with maxStalenessSeconds'126 );127 }128 129 if (this.hedge) {130 throw new MongoInvalidArgumentError(131 'Primary read preference cannot be combined with hedge'132 );133 }134 }135 }136 137 // Support the deprecated `preference` property introduced in the porcelain layer138 get preference(): ReadPreferenceMode {139 return this.mode;140 }141 142 static fromString(mode: string): ReadPreference {143 return new ReadPreference(mode as ReadPreferenceMode);144 }145 146 /**147 * Construct a ReadPreference given an options object.148 *149 * @param options - The options object from which to extract the read preference.150 */151 static fromOptions(options?: ReadPreferenceFromOptions): ReadPreference | undefined {152 if (!options) return;153 const readPreference =154 options.readPreference ?? options.session?.transaction.options.readPreference;155 const readPreferenceTags = options.readPreferenceTags;156 157 if (readPreference == null) {158 return;159 }160 161 if (typeof readPreference === 'string') {162 return new ReadPreference(readPreference, readPreferenceTags, {163 maxStalenessSeconds: options.maxStalenessSeconds,164 hedge: options.hedge165 });166 } else if (!(readPreference instanceof ReadPreference) && typeof readPreference === 'object') {167 const mode = readPreference.mode || readPreference.preference;168 if (mode && typeof mode === 'string') {169 return new ReadPreference(mode, readPreference.tags ?? readPreferenceTags, {170 maxStalenessSeconds: readPreference.maxStalenessSeconds,171 hedge: options.hedge172 });173 }174 }175 176 if (readPreferenceTags) {177 readPreference.tags = readPreferenceTags;178 }179 180 return readPreference as ReadPreference;181 }182 183 /**184 * Replaces options.readPreference with a ReadPreference instance185 */186 static translate(options: ReadPreferenceLikeOptions): ReadPreferenceLikeOptions {187 if (options.readPreference == null) return options;188 const r = options.readPreference;189 190 if (typeof r === 'string') {191 options.readPreference = new ReadPreference(r);192 } else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {193 const mode = r.mode || r.preference;194 if (mode && typeof mode === 'string') {195 options.readPreference = new ReadPreference(mode, r.tags, {196 maxStalenessSeconds: r.maxStalenessSeconds197 });198 }199 } else if (!(r instanceof ReadPreference)) {200 throw new MongoInvalidArgumentError(`Invalid read preference: ${r}`);201 }202 203 return options;204 }205 206 /**207 * Validate if a mode is legal208 *209 * @param mode - The string representing the read preference mode.210 */211 static isValid(mode: string): boolean {212 const VALID_MODES = new Set([213 ReadPreference.PRIMARY,214 ReadPreference.PRIMARY_PREFERRED,215 ReadPreference.SECONDARY,216 ReadPreference.SECONDARY_PREFERRED,217 ReadPreference.NEAREST,218 null219 ]);220 221 return VALID_MODES.has(mode as ReadPreferenceMode);222 }223 224 /**225 * Validate if a mode is legal226 *227 * @param mode - The string representing the read preference mode.228 */229 isValid(mode?: string): boolean {230 return ReadPreference.isValid(typeof mode === 'string' ? mode : this.mode);231 }232 233 /**234 * Indicates that this readPreference needs the "SecondaryOk" bit when sent over the wire235 * @see https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol/#op-query236 */237 secondaryOk(): boolean {238 const NEEDS_SECONDARYOK = new Set<string>([239 ReadPreference.PRIMARY_PREFERRED,240 ReadPreference.SECONDARY,241 ReadPreference.SECONDARY_PREFERRED,242 ReadPreference.NEAREST243 ]);244 245 return NEEDS_SECONDARYOK.has(this.mode);246 }247 248 /**249 * Check if the two ReadPreferences are equivalent250 *251 * @param readPreference - The read preference with which to check equality252 */253 equals(readPreference: ReadPreference): boolean {254 return readPreference.mode === this.mode;255 }256 257 /** Return JSON representation */258 toJSON(): Document {259 const readPreference = { mode: this.mode } as Document;260 if (Array.isArray(this.tags)) readPreference.tags = this.tags;261 if (this.maxStalenessSeconds) readPreference.maxStalenessSeconds = this.maxStalenessSeconds;262 if (this.hedge) readPreference.hedge = this.hedge;263 return readPreference;264 }265}266 