All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mongodb.kotlin.client.model.Properties.kt Maven / Gradle / Ivy

There is a newer version: 5.3.0
Show newest version
/*
 * Copyright 2008-present MongoDB, Inc.
 * Copyright (C) 2016/2022 Litote
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @custom-license-header
 */
package com.mongodb.kotlin.client.model

import com.mongodb.kotlin.client.property.KCollectionSimplePropertyPath
import com.mongodb.kotlin.client.property.KMapSimplePropertyPath
import com.mongodb.kotlin.client.property.KPropertyPath
import com.mongodb.kotlin.client.property.KPropertyPath.Companion.CustomProperty
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
import kotlin.reflect.full.findParameterByName
import kotlin.reflect.full.primaryConstructor
import kotlin.reflect.jvm.internal.ReflectProperties.lazySoft
import kotlin.reflect.jvm.javaField
import org.bson.codecs.pojo.annotations.BsonId
import org.bson.codecs.pojo.annotations.BsonProperty

private val pathCache: MutableMap by lazySoft { ConcurrentHashMap() }

/** Returns a composed property. For example Friend::address / Address::postalCode = "address.postalCode". */
public operator fun  KProperty1.div(p2: KProperty1): KProperty1 =
    KPropertyPath(this, p2)

/**
 * Returns a composed property without type checks. For example Friend::address % Address::postalCode =
 * "address.postalCode".
 */
public operator fun  KProperty1.rem(p2: KProperty1): KProperty1 =
    KPropertyPath(this, p2)

/**
 * Returns a collection composed property. For example Friend::addresses / Address::postalCode = "addresses.postalCode".
 */
@JvmName("divCol")
public operator fun  KProperty1?>.div(p2: KProperty1): KProperty1 =
    KPropertyPath(this, p2)

/** Returns a map composed property. For example Friend::addresses / Address::postalCode = "addresses.postalCode". */
@JvmName("divMap")
public operator fun  KProperty1?>.div(
    p2: KProperty1
): KProperty1 = KPropertyPath(this, p2)

/**
 * Returns a mongo path of a property.
 *
 * The path name is computed by checking the following and picking the first value to exist:
 * - SerialName annotation value
 * - BsonId annotation use '_id'
 * - BsonProperty annotation
 * - Property name
 */
internal fun  KProperty.path(): String {
    return if (this is KPropertyPath<*, T>) {
        this.name
    } else {
        pathCache.computeIfAbsent(this.toString()) {

            // Check serial name - Note kotlinx.serialization.SerialName may not be on the class
            // path
            val serialName =
                annotations.firstOrNull { it.annotationClass.qualifiedName == "kotlinx.serialization.SerialName" }
            var path =
                serialName?.annotationClass?.members?.firstOrNull { it.name == "value" }?.call(serialName) as String?

            // If no path (serialName) then check for BsonId / BsonProperty
            if (path == null) {
                val originator = if (this is CustomProperty<*, *>) this.previous.property else this
                // If this property is calculated (doesn't have a backing field) ex
                // "(Student::grades / Grades::score).posOp then
                // originator.javaField will NPE.
                // Only read various annotations on a declared property with a backing field
                if (originator.javaField != null) {
                    val constructorProperty =
                        originator.javaField!!.declaringClass.kotlin.primaryConstructor?.findParameterByName(this.name)

                    // Prefer BsonId annotation over BsonProperty
                    path = constructorProperty?.annotations?.filterIsInstance()?.firstOrNull()?.let { "_id" }
                    path =
                        path ?: constructorProperty?.annotations?.filterIsInstance()?.firstOrNull()?.value
                }
                path = path ?: this.name
            }
            path
        }
    }
}

/** Returns a collection property. */
public val  KProperty1?>.colProperty: KCollectionSimplePropertyPath
    get() = KCollectionSimplePropertyPath(null, this)

/** In order to write array indexed expressions (like `accesses.0.timestamp`). */
public fun  KProperty1?>.pos(position: Int): KPropertyPath =
    colProperty.pos(position)

/** Returns a map property. */
public val  KProperty1?>.mapProperty: KMapSimplePropertyPath
    get() = KMapSimplePropertyPath(null, this)

@Suppress("MaxLineLength")
/**
 * [The positional array operator $ (projection or update)](https://docs.mongodb.com/manual/reference/operator/update/positional/)
 */
public val  KProperty1?>.posOp: KPropertyPath
    get() = colProperty.posOp

@Suppress("MaxLineLength")
/** [The all positional operator $[]](https://docs.mongodb.com/manual/reference/operator/update/positional-all/) */
public val  KProperty1?>.allPosOp: KPropertyPath
    get() = colProperty.allPosOp

@Suppress("MaxLineLength")
/**
 * [The filtered positional operator $[\]](https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/)
 */
public fun  KProperty1?>.filteredPosOp(identifier: String): KPropertyPath =
    colProperty.filteredPosOp(identifier)

/** Key projection of map. Sample: `p.keyProjection(Locale.ENGLISH) / Gift::amount` */
@Suppress("UNCHECKED_CAST")
public fun  KProperty1?>.keyProjection(key: K): KPropertyPath =
    mapProperty.keyProjection(key) as KPropertyPath




© 2015 - 2025 Weber Informatics LLC | Privacy Policy