litote / kmongo Goto Github PK
View Code? Open in Web Editor NEW[deprecated] KMongo - a Kotlin toolkit for Mongo
Home Page: https://litote.org/kmongo/
License: Apache License 2.0
[deprecated] KMongo - a Kotlin toolkit for Mongo
Home Page: https://litote.org/kmongo/
License: Apache License 2.0
Kotlin 1.2.0 is out! Once more, the quick Kotlin comes out with a new version.. I see that kmongo just recently jumped from reflection-1.1.50 in its version 3.5.0 to reflection-1.1.60 in 3.5.1, but still, Kotlin with its steady pace, is ahead again!
Could you please update Kotlin version so that to exclude the exclude inclusions in the gradle project file of a project using kmongo? It's actually a challenge to keep up with every new verison that comes out so often.
Thanks!
Need to use isAccessible = true
The problem is that when an entity class contains a field of BigDecimal type, it is persisted into the database with a field of Double type, but it should be Decimal type (from MongoDB 3.4 version). If the standard MongoDB driver is used, without KMongo, BigDecimal is persisted with a field of Decimal type, so the problem is only in KMongo.
Please, fix it.
Fantastic module, thanks!
I use the async version. The callbacks aren't composable / chainable.
So I've ended up writing extension methods wrapping all existing methods on MongoCollection, to return rx-java equivalents: Single, Observable.
Would be great if KMongo could provide similar.
Hi! Consider simple data class data class User(val isActive:Boolean)
. When stored it being saved as {active: true}
. isActive
becomes active
. It looks odd, it's not very predictable and I always should check what should I write in the query in this case. Is it a bug or maybe it's configurable behavior? Thanks!
I just started using KMongo in my open source project. Now I want to write tests using KotlinTest but KMongo with a real MongoDB makes test executions very slow. I can use mocking but I try to avoid this where possible. After searching the internet I found the Fongo project (fake mongo) which is an in-memory java implementation of MongoDB and intercepts calls to the standard mongo-java-driver. That sounds pretty cool and if KMongo could support Fongo out of the box, it would be possible to use in-memory databases for testing purposes without changing the production code.
getCollection -> getCollectionOfName
mapReduce -> mapReduceWith
listIndexes -> listTypedIndexes
kotlin.reflect.KotlinReflectionInternalError: Reflection on built-in Kotlin types is not yet fully supported. No metadata found for public final val name: String defined in kotlin.Enum[DeserializedPropertyDescriptor@7803bfd]
at kotlin.reflect.jvm.internal.RuntimeTypeMapper.mapPropertySignature(RuntimeTypeMapper.kt:206)
at kotlin.reflect.jvm.internal.DescriptorBasedProperty.<init>(DescriptorBasedProperty.kt:36)
at kotlin.reflect.jvm.internal.KProperty1Impl.<init>(KProperty1Impl.kt:26)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.createProperty(KDeclarationContainerImpl.kt:97)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.access$createProperty(KDeclarationContainerImpl.kt:33)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$getMembers$visitor$1.visitPropertyDescriptor(KDeclarationContainerImpl.kt:64)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$getMembers$visitor$1.visitPropertyDescriptor(KDeclarationContainerImpl.kt:52)
at kotlin.reflect.jvm.internal.impl.descriptors.impl.PropertyDescriptorImpl.accept(PropertyDescriptorImpl.java:336)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$getMembers$2.invoke(KDeclarationContainerImpl.kt:81)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$getMembers$2.invoke(KDeclarationContainerImpl.kt:33)
at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:137)
(...)
While it is great that this can be configured, I find it odd that the default behavior is all lowercase which is pretty much the wrong thing to do by every standard I've seen.
I do understand that mongo has an unclear standard around this but the contradiction is around snake_case (which is what mongo itself uses internally) and camelCase (which is what the actual verbiage written implies when it says to use javascript standards).
I personally believe that the default behavior should be changed to one of those 2 things.
Arguments can be made for using camel case (many of the other popular mongo libraries default things to camel case) and it is basically doing a .decapitalize() on the class name. I'm guessing there are probably still others which use snake case because that is what mongo does internally as well and even that would make sense to utilize.
But the issue I see is that defaulting it to simply be all lower case is a bad default behavior to be using.
But again, I am super happy that you made it extremely easy for me to change that logic so it isn't a show stopper for anyone. Just an observation of a change that I believe should be made for consistency and readability for anyone actually trying to work with it using a non-kmongo library (basically every other library I've personally worked with does the camel case decapitalizing the first letter).
When used in Bson query context, serializing object in non object json (like java.util.Locale (Locale -> String)) throws Exception
@zigzago
Is there any roadmap for 3.4.0?
I believe i can help
Class Hierarchy is not handled right
data class ShipDetail(
var _id: String? = null,
var order_id: String? = null)
val collection = database.getCollection<ShipDetail>("ship_detail")
var r = collection.findOne();
produce exception:
Exception in thread "main" org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class ShipDetail.
at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37)
at com.mongodb.FindIterableImpl.createQueryOperation(FindIterableImpl.java:228)
at com.mongodb.FindIterableImpl.execute(FindIterableImpl.java:224)
at com.mongodb.FindIterableImpl.first(FindIterableImpl.java:205)
at org.litote.kmongo.ExtensionsKt.findOne(extensions.kt:163)
at org.litote.kmongo.ExtensionsKt.findOne$default(extensions.kt:162)
I haven't looked to see if this is kmongo or vanilla mongo's fault but am just presuming it is on kmongo's end right now.
It looks like when I goto save a list of DbRef they get saved as natural objects in mongo instead of DbRef in mongo.
For example,
data class MongoReportDefinition(val _id: ObjectId, val title: String, val columns: List<DBRef>, val filters: List<Filter>)
val saveCard = MongoReportDefinition(_id = ObjectId(card.id),
title = card.title,
columns = card.columns.map { DBRef("columnDefinition", it.id) },
filters = card.filters)
db.getCollection<MongoReportDefinition>("reportDefinition")
.withWriteConcern(WriteConcern.JOURNALED)
.save(saveCard)
In the database gets saved (viewed through mongo shell) as
{
"_id" : ObjectId("58ebf9a6a00a931d04b5290d"),
"title" : "Shipped Assets",
"columns" : [
{
"id" : "orderAsset.orderNumber",
"collectionName" : "columnDefinition",
"databaseName" : null
},
{
"id" : "orderAsset.asset.serialNumber",
"collectionName" : "columnDefinition",
"databaseName" : null
},
{
"id" : "orderAsset.line.name",
"collectionName" : "columnDefinition",
"databaseName" : null
},
{
"id" : "orderAsset.line.description",
"collectionName" : "columnDefinition",
"databaseName" :null
}
],
"filters" : [
{
"field" : "line.lineStatus",
"operation" : "EQ",
"value" : "Closed",
"options" : null,
"label" : null
},
{
"field" : "asset.serialNumber",
"operation" : "NEQ",
"value" : null,
"options" : null,
"label" : null
}
]
}
expected behavior was
{
"_id" : ObjectId("58ebf9a6a00a931d04b5290d"),
"title" : "Shipped Assets",
"columns" : [
DBRef("columnDefinition", "orderAsset.orderNumber"),
DBRef("columnDefinition", "orderAsset.asset.serialNumber"),
DBRef("columnDefinition", "orderAsset.line.name"),
DBRef("columnDefinition", "orderAsset.line.description"),
],
"filters" : [
{
"field" : "line.lineStatus",
"operation" : "EQ",
"value" : "Closed",
"options" : null,
"label" : null
},
{
"field" : "asset.serialNumber",
"operation" : "NEQ",
"value" : null,
"options" : null,
"label" : null
}
]
}
The subject is an error that happens technically inside the mongo-java-driver not kmongo. However, what I believe will be of immense help to everyone (given the amount of times this question is asked and the answer is there is no answer), I believe kmongo can provide a nice clean solution to the problem at hand with its extensions.
The reason the above error happens apparently has to do with the "order" that data is stored in mongo. So for example if you have a collection called Tenant with data as
{ "_id" : 42, "url" : "somewhere.com", "updated" : ISODate("2016-07-29T14:32:55.123Z"), "title" : "United States Post Office" }
And if you have a data class like
data class Tenant(@MongoId val id: Int, val title: String?)
There is a conflict because after the _id it is expecting a title not a url as is the case above. If the data is actually stored as _id first and title second then everything works as expected.
As I said the above is a mongo error. However, what would be extremely helpful is to allow a work around or fallback. Since there are already reified helper for kotlin support, the reified helper can actually wrap the real getCollection method with a try/catch for this specific error and in this scenario and create the bean instead using the normal extendedJsonMapper on the original document format of that record.
Or if that is just too complicated, just have an extentions hook like
MongoDatabase.getCollectionAs() which would effectively do a normal getCollection call without providing it the auto-transformation, grab the resulting data as the raw mongo document, and then doing an extendedJsonMapper on that results data.json to cast it using the normal JsonSerializer instead of the BsonSerializer (at least until the year+ long bug has been resolved).
Not sure if any of this made sense but ideally it might be something like
val collection = mongodb.getCollectionAs().find()
does the same thing as
val collection = mongodb.getCollection().find()
except that its underlying implementation is to use the extendedJsonMapper instead of letting native mongo use the BsonMapper (which is not supportive of data being in different order). In theory it is slower and uses more memory (have to serialize/deserialize everything instead of only deserializing) but in a rough POC (having to do this same sort of thing to work around the bug now), I'm seeing no actual difference in performance for large sets of data.
Let's say, I have such objects:
data class Planet(_id: ObjectId, name: String)
data class User(_id: ObjectId, name: String, planets: List<Planet>)
I needed put User to document
Json became something like that:
{
"id": "1111111",
"name": "Foo",
"planets": [
{
"_id": "2222222",
"name": "Earth"
}
]
}
Insert works perfectly nice. MongoDB also works nice. I can't find any information why this should be not OK.
However, on an update, all _id
are cleared. This is done in setModifier.
Is possible to fix it?
Documentation has section talking about "You just have to add an import org.litote.kmongo.KMongoOperator.*", but this class does not exist.
Whenever you pass ObjectNode as value for update/findAndUpdate it gets serialized as array.
val objectNode : ObjectNode = jacksonObjectMapper().createObjectNode()
objectNode.put("test", 123)
database.getCollection("test").updateOne(Filters.eq("_id", "test"), Updates.set("objectField", objectNode), UpdateOptions().upsert(true))
results in { "_id" : "test", "objectField" : [ [ ] ] }
LocalDateTime, LocalDate, LocalTime, ZonedDateTime, OffsetDateTime and OffsetTime all depend of the local timezone.
Our system includes a test that verifies all of our model classes have a ID field recognizable by KMongo. However, KMongo doesn't make public any method which we can use to check this. Our current solution is copying the internal object MongoIdUtil into our code.
For use cases like this, might I suggest that KMongo expose some public method for determining whether it can find an ID field on a model class.
Example test (KotlinTest)
class EntityModelSpec : ShouldSpec() {
init {
ClasspathScanningUtil.classesExtending(EntityModel::class).forEach {
it.simpleName!! {
should("have an ID property recognizable by KMongo") {
MongoIdUtil.findIdProperty(it) shouldNotBe null
}
}
}
}
}
getCollection -> getCollectionOfName
mapReduce -> mapReduceWith
listIndexes -> listTypedIndexes
dropIndex -> dropIndexOfKeys
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
kotlin/reflect/jvm/internal/ReflectionFactoryImpl.function(Lkotlin/jvm/internal/FunctionReference;)Lkotlin/reflect/KFunction; @5: invokestatic
Reason:
Type 'kotlin/jvm/internal/FunctionReference' (current frame, stack[2]) is not assignable to 'kotlin/jvm/internal/CallableReference'
Current Frame:
bci: @5
flags: { }
locals: { 'kotlin/reflect/jvm/internal/ReflectionFactoryImpl', 'kotlin/jvm/internal/FunctionReference' }
stack: { uninitialized 0, uninitialized 0, 'kotlin/jvm/internal/FunctionReference' }
Bytecode:
0x0000000: bb00 3b59 2bb8 0053 2bb6 0059 2bb6 005c
0x0000010: 2bb6 0060 b700 63b0
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at kotlin.jvm.internal.Reflection.<clinit>(Reflection.java:32)
at org.litote.kmongo.util.KMongoConfiguration.<clinit>(KMongoConfiguration.kt)
at org.litote.kmongo.KMongo.configureRegistry(KMongo.kt:218)
at org.litote.kmongo.KMongo.configureOptions(KMongo.kt:215)
at org.litote.kmongo.KMongo.createClient(KMongo.kt:104)
at org.litote.kmongo.KMongo.createClient(KMongo.kt:80)
at org.litote.kmongo.KMongo.createClient(KMongo.kt:50)
This issue only happens only when I run the app from jar, it works fine in the IDE. My Kotlin version is 1.1.1
Looks like if you try and do a save using something like the below example
val id = ObjectId("58ed213ca00a936d64541a1d")
val thing = Document("_id", id).append("name", "test this")
db.getCollection("test").save(thing)
The first time through it saves a brand new document correctly. The second time through it throws a duplicate key exception.
In tracking down the code, it looks like the getIdProperty method doesn't have support for any kind of "map" structure. Assumes everything is always a proper object. If the type is a map it should look for a key called "_id" instead of a method called get_id.
filterIdToExtendedJson tries to filter out "_id" field from "update" by scanning for MongoId annotation and fails with internal "kotlin.reflect" exception.
KMongo 3.3.2, kotlin 1.0.3
kotlin.reflect.KotlinReflectionInternalError: Reflection on built-in Kotlin types is not yet fully supported. No metadata found for public open val length: [Not-computed] defined in kotlin.String[DeserializedPropertyDescriptor@476c17a]
at kotlin.reflect.jvm.internal.RuntimeTypeMapper.mapPropertySignature(RuntimeTypeMapper.kt:206)
at kotlin.reflect.jvm.internal.DescriptorBasedProperty.<init>(DescriptorBasedProperty.kt:36)
at kotlin.reflect.jvm.internal.KProperty1Impl.<init>(KProperty1Impl.kt:26)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.createProperty(KDeclarationContainerImpl.kt:97)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.access$createProperty(KDeclarationContainerImpl.kt:33)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$getMembers$visitor$1.visitPropertyDescriptor(KDeclarationContainerImpl.kt:64)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$getMembers$visitor$1.visitPropertyDescriptor(KDeclarationContainerImpl.kt:52)
at kotlin.reflect.jvm.internal.impl.descriptors.impl.PropertyDescriptorImpl.accept(PropertyDescriptorImpl.java:336)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$getMembers$2.invoke(KDeclarationContainerImpl.kt:81)
at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$getMembers$2.invoke(KDeclarationContainerImpl.kt:33)
at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:137)
at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:98)
at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:121)
at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:97)
at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:121)
at kotlin.sequences.SequencesKt___SequencesKt.toCollection(_Sequences.kt:527)
at kotlin.sequences.SequencesKt___SequencesKt.toMutableList(_Sequences.kt:551)
at kotlin.sequences.SequencesKt___SequencesKt.toList(_Sequences.kt:544)
at kotlin.reflect.KClasses.getMemberProperties(KClasses.kt:142)
at org.litote.kmongo.util.MongoIdUtil.getAnnotatedMongoIdProperty(MongoIdUtil.kt:44)
at org.litote.kmongo.util.MongoIdUtil.findIdProperty(MongoIdUtil.kt:38)
at org.litote.kmongo.jackson.FilterIdModule$IdPropertyFilter.include(FilterIdModule.kt:37)
at org.litote.kmongo.jackson.FilterIdModule$IdPropertyFilter.serializeAsField(FilterIdModule.kt:41)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFilteredFields(MapSerializer.java:809)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:529)
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:30)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:292)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3672)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3048)
at org.litote.kmongo.util.KMongoUtil.filterIdToExtendedJson(KMongoUtil.kt:104)
at org.litote.kmongo.util.KMongoUtil.setModifier(KMongoUtil.kt:113)
at org.litote.kmongo.ExtensionsKt.updateOneById(extensions.kt:432)
It'd be great if insert would return a generic T or like Update and Delete a specialized result class. In this case if I insert an element and I want to return it I have to make a find after an insert. In PyMongo for example it's one call and insert returns the element.
I would like to convert a JSON string to an DBObject and store it into my database. I wrote this code :
val jsonInput = "{ \"ok\": 42 }";
val objToStore = ObjWrapper(JSON.parse(jsonInput) as DBObject)
ObjectMapper().registerModule(KotlinModule()).writeValueAsString(objToStore) // works
col.insertOne(objToStore) // throws an exception
When I used Jackson directly with the KotlinModule, it works, but when I try to insert the object I obtain the following exception :
java.io.UncheckedIOException: com.fasterxml.jackson.databind.JsonMappingException: Cannot infer visibility for inherited final val size: kotlin.Int defined in java.util.LinkedHashMap[JavaPropertyDescriptor@6b9267b] (through reference chain: com.test.ObjWrapper["value"])
Perhaps there is a better solution to the problem but for some reason it thinks that an org.json.Document (a java class) translates to Any instead of Any? for the value. Because of this oddity if I ever do a save on a document containing a "null" value it errors out with an error similar to
Exception in thread "Thread-3" com.fasterxml.jackson.databind.JsonMappingException: Parameter specified as non-null is null: method org.litote.kmongo.jackson.FilterIdModule$IdPropertyFilter.serializeAsField, parameter pojo (through reference chain: com.example.WorkQueuePayload["payload"]->org.bson.Document["u_role"])
Where the document should look something like
{
"_id" : "some id string",
"payload" : {
"name" : "John Doe",
"u_role" : null,
"closed_at" : ISODate("2016-10-09T07:00:05Z"),
}
}
So doing a save on an object that has a payload type of Document? which happens to have a null value contained inside of it causes the issue.
Ideally it would be far better (cause I'm sure there will be other scenarios where I have to use a non-kotlin data object and the translation of Object to Any instead of Any? will become problematic) would be to support the correct conversion of Object to Any? thus allowing null to be a valid value.
Assuming that this is a Kotlin failing and not kmongo limitation (or even as a nice addon for any other reason), it would be nice is save had an optional flag that could be passed on whether or not to bypass the document validation (hoping that perhaps that will solve the problem maybe).
Load annotations on parameters of primary constructor
Testing only with the latest published SNAPSHOT of code.
Looks like if a DBRef is being saved and the "id" portion of the DBRef happens to be an ObjectId that the systems throws an org.bson.BsonSerializationException: Document size must be at least 5
exception.
Take the following scenario
data class TestData(val title: String, val ref: DBRef)
// works
db.getCollection<TestData>.save(TestData("I'm working", DBRef("magic", "smoke")))
// broken
db.getCollection<TestData>.save(TestData("I'm working", DBRef("magic", ObjectId())))
Took me a while to track this one down but it looks like there is an inconsistent helper logic on the extensions code for updateOneById
In particular, it seems that if you pass in a string you are expected to provide the $set as part of the string (which absolutely makes perfect sense as well). However, if you pass in a Bson object then it seems to automatically put that inside of a $set which is 1) inconsistent behavior and 2) prevents you from being able to do an $unset call on an update.
For example,
// valid
collection.updateOneById(42, "{$set: {a: 'apple', b: 'banana'}, $unset: {c: 1}}")
// should be the same as above but actually translates to {$set: {$set: {a: 'apple', b: 'banana'}, $unset: {c: 1}}} causing an error
collection.updateOneById(42, Document("\$set", Document("a", "apple").append("b", "banana"))
.append("\$unset", Document("c", 1)))
Hi. I'm trying to update a single document by it's id.
Mongo shell query looks like this:
db.testCollection.updateOne({"_id": ObjectId("5a36cfe38c699050c8913dea")}, { $addToSet: { a: "somevalue"}, $push: {b:"someothervalue"}})
Kmongo query looks like this:
db.getCollection<TestCollection>()
.updateOneById(value.id,
KeyValues(TestCollection::a addToSet "somevalue", TestCollection::b push "someothervalue"))
Note: I'm using custom classes KeyValues and KeyValue with extensions on KProperty1 classes for typesafe operations.
The problem is that KMongoUtil.setModifier method is called before these classes are serialized to Bson(or Map) and KMongoUtil.UPDATE_OPERATORS don't contain array update operators.
So, the update after setModifier method looks like: {$set: { $addToSet: { a: "somevalue"}, $push: {b:"someothervalue"}}}
which is incorrect.
Am I doing something wrong?
It seems that if a field is marked with @transient annotation (the kotlin.jvm.Transient version), the field is still be persisted when being saved to the database.
A work-around right now is to mark it with @JsonIgnore but it would be really nice to not have to embed jackson specific annotations in order to get the core languages designed support for not serializing fields to work.
Example
data class Activity(var activity: String, var date: Date = Date(), var reference: Any? = null) {
@Transient var transactionId: Int = 0
}
Saving that results in
{
"activity" : "Transferred to Reserve",
"date" : ISODate("2017-05-09T21:10:48Z"),
"transactionId": 0
}
I noticed while utilizing your API's that you don't have any support for write concerns. Is there any way you can possibly add that as another parameter on the insert/update/delete functions leaving it with a default of null which utilizes the DBCollections.getWriteConcern()
This is especially important for my project as we have certain very specific areas which require a different write concern then is the default concern.
If needed I'll be happy to fork the code, make the necessary modifications, and submit a pull request.
Hi I am the author of https://github.com/beyondeye/kmongodsl
I would be interested to integrate it in kmongo. Also I will be happy to get suggestions about the preferred syntax. (the current one is still a work in progress)
Very Great Library!
Thanks @zigzago for providing this for us.
Inspired by issue #22 i start to fork and create sub module for kmongo-coroutine, it basically wrap kmongo-async with coroutine. The sub module already cover all of extension functions and several unit tests, i have a plan to cover all unit tests this week.
How do you think @zigzago?
I will create pull request after this.
E.g. the extension function
public fun <T> MongoCollection<T>.findOneAndReplace(filter: String, replacement: T, options: FindOneAndReplaceOptions): T
defined in org.litote.kmongo in file ExtensionsKt.class says about the return value
If no documents matched the query filter, then null will bereturned
If this is true then the return type should be T?
using KMongo 3.4.1
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.