redisearch / lettusearch Goto Github PK
View Code? Open in Web Editor NEWLettuce-based client for RediSearch
Home Page: https://redisearch.io
License: Apache License 2.0
Lettuce-based client for RediSearch
Home Page: https://redisearch.io
License: Apache License 2.0
When running below query in redis cli:
FT.AGGREGATE <index_name>* GROUPBY 1 @field REDUCE TOLIST 1 @field1 as list REDUCE COUNT 0 as count
Result:
But when we run through lettusearch :
Result: (AggregateResults)
{
"field": "value",
"list": "field1_value1"
"field1_value2": "count"
}
It should be:
{
"field": "value",
"list": "field1_value1","field1_value2"
"count": 5
}
RediSearchReactiveCommands<String, Object> doesn't compile. The exception is Incompatible types: RediSearchReactiveCommands<String, String> required. Is it not possible to specify other than a String as value?
FT.CREATE FT.CREATE" "group-idx" "PREFIX" "1" "group:" "SCHEMA" "TAG" "location.country" AS "country"
It 'll be grate to have possability make smth like:
Field.tag("location.country").alias("country").build();
Currently, is there a way to set the scoring function for a search?
I am trying to connect to the sentinel node of a Redis and I am getting the error:
Caused by: io.lettuce.core.RedisConnectionException: null
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:75) ~[lettuce-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:56) ~[lettuce-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
at io.lettuce.core.AbstractRedisClient.getConnection(AbstractRedisClient.java:242) ~[lettuce-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
at com.redislabs.lettusearch.RediSearchClient.connect(RediSearchClient.java:101) ~[lettusearch-2.3.2.jar:na]
at com.redislabs.lettusearch.RediSearchClient.connect(RediSearchClient.java:94) ~[lettusearch-2.3.2.jar:na]
at ru.vtb.payments.catalog.config.datasouce.RedisConfig.rediSearchReactiveCommands(RedisConfig.kt:27) ~[main/:na]
at ru.vtb.payments.catalog.config.datasouce.RedisConfig$$EnhancerBySpringCGLIB$$7f1572ea.CGLIB$rediSearchReactiveCommands$1(<generated>) ~[main/:na]
at ru.vtb.payments.catalog.config.datasouce.RedisConfig$$EnhancerBySpringCGLIB$$7f1572ea$$FastClassBySpringCGLIB$$173fceee.invoke(<generated>) ~[main/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at ru.vtb.payments.catalog.config.datasouce.RedisConfig$$EnhancerBySpringCGLIB$$7f1572ea.rediSearchReactiveCommands(<generated>) ~[main/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
... 82 common frames omitted
Caused by: io.lettuce.core.RedisCommandExecutionException: NOAUTH Authentication required.
at io.lettuce.core.ExceptionFactory.createExecutionException(ExceptionFactory.java:135) ~[lettuce-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
at io.lettuce.core.ExceptionFactory.createExecutionException(ExceptionFactory.java:108) ~[lettuce-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
at io.lettuce.core.RedisPublisher$SubscriptionCommand.complete(RedisPublisher.java:764) ~[lettuce-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
docker-compose.yml to launch redis containers:
version: '2'
networks:
app-tier:
driver: bridge
services:
redis:
image: 'redislabs/redisearch:latest'
environment:
- REDIS_PASSWORD=12345678
ports:
- '6379:6379'
networks:
- app-tier
redis-sentinel:
image: 'bitnami/redis-sentinel:latest'
environment:
- REDIS_MASTER_HOST=127.0.0.1
- REDIS_MASTER_PASSWORD=12345678
- REDIS_SENTINEL_PASSWORD=12345678
ports:
- '26379:26379'
networks:
- app-tier
The settings in the spring-boot app to connect Redis:
redis:
port: 6379
database: 0
host: localhost
mutex-timeout: 300s
command-timeout: 10s
sentinel:
master: mymaster
nodes: 127.0.0.1:26379
password: 12345678
password: 12345678
I see that the redisURI in the RediSearchClient is formed correctly
I also see a connection that is formed in the RediSearchClient, it does not contain a password, what is this password?
I tried to set it in debug, but the authentication error remained.
The password to Redis is correct:
I checked that the password to Redis is correct and also when I configure sentinel Redis without password there are no connection errors.
I see similar issues:
Various lettuce core classes are copied into this library (with same name and package). This seems problematic. I was debugging why ftInfo throws a NumberFormatException and it appears that the copy of LettuceStrings in this package has been modified to deal with it, but, the version of LettuceStrings actually being used (by random classpath ordering) is that from the lettuce package which does not have this condition:
if ("-nan".equals(s)) {
return Double.NaN;
}
ft.info snippet with -nan
44) 1) "bytes_collected"
2) "0"
3) "total_ms_run"
4) "0"
5) "total_cycles"
6) "0"
7) "avarage_cycle_time_ms"
8) "-nan"
9) "last_run_time_ms"
10) "0"
11) "gc_numeric_trees_missed"
12) "0"
13) "gc_blocks_denied"
14) "0"
Exception:
java.lang.NumberFormatException: For input string: "-nan"
at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054) ~[na:na]
at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110) ~[na:na]
at java.base/java.lang.Double.parseDouble(Double.java:543) ~[na:na]
at io.lettuce.core.internal.LettuceStrings.toDouble(LettuceStrings.java:97) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.lettuce.core.protocol.RedisStateMachine.readFloat(RedisStateMachine.java:627) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.lettuce.core.protocol.RedisStateMachine.handleFloat(RedisStateMachine.java:425) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.lettuce.core.protocol.RedisStateMachine$State$Type.handle(RedisStateMachine.java:206) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.lettuce.core.protocol.RedisStateMachine.doDecode(RedisStateMachine.java:334) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.lettuce.core.protocol.RedisStateMachine.decode(RedisStateMachine.java:295) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:799) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.lettuce.core.protocol.CommandHandler.decode0(CommandHandler.java:750) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:724) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:618) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:560) ~[lettuce-core-6.0.0.RELEASE.jar:6.0.0.RELEASE]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) ~[netty-transport-4.1.52.Final.jar:4.1.52.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.52.Final.jar:4.1.52.Final]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
Hi, I want to use INKEYS parameter to restrict RediSearch results by a document ID.
Example in redis-cli:
FT.SEARCH SAMPLE_INDEX "(@channels:{test})" INKEYS 1 4
Is there any way to search like that with lettusearch? I don't see that it is possible to specify this parameter in com.redislabs.lettusearch.search.SearchOptions
While using this client library, I noticed that you can create a FT.SEARCH query with a multi-fields SORTBY with no errors whereas the backend does not support it.
Here is an example:
final SearchResults<String, String> searchResult = connection.sync().search(
"post", queryString,
SearchOptions.builder()
.limit(Limit.builder().num(limit).build())
.sortBy(SortBy.builder()
.field("pinned").direction(Direction.Descending)
.field("publicationTime").direction(Direction.Descending)
.field("updateTime").direction(Direction.Descending).build()).build());
It seems that only the last SortBy field is considered.
There is maybe a reason I am not aware of, but as a user, I was expecting a compilation error or at least an error at runtime.
while creating an index getting error
ERR unknown command
FT.CREATE
redis-cli --cluster call 127.0.0.1:30001 FT.CREATE myIdx SCHEMA title TEXT WEIGHT 5.0 body TEXT url TEXT value NUMERIC
i tried to install the redis search module using below command, but no success.
redis-cli --cluster call 127.0.0.1:30001 MODULE load redisearch.so OPT1 OPT2
When using two argument sugget method
List<Suggestion<V>> sugget(K key, V prefix);
instead of
List<Suggestion<V>> sugget(K key, V prefix, SuggetOptions options);
Default SuggetOptions is not passed, which results in NLP
Recently, Redisearch added contains filters on aggregate queries, e.g.:
FT.AGGREGATE idx * FILTER "contains(@name, 'åæø')"
However, when using aggregate queries with lettusearch, it seems that the StringCodec
is not respected unlike when using a select string.
More specifically, the filter string will be added with RediSearchCommandArgs::add
, whereas the values of select strings are added with RediSearchCommandArgs::addValue
. The former does not seem to respect the StringCodec
used. As such, the query I gave above is not possible with lettusearch as far as I can tell since UTF-8 is used.
SearchOptions with noContent set as true returns array of empty objects.
But as per redis documentation it should return list of document ids.
NOCONTENT : If it appears after the query, we only return the document ids and not the content. This is useful if RediSearch is only an index on an external document collection
Steps for reproduce:
FT.CREATE idx ON HASH PREFIX 1 my_prefix: SCHEMA category TAG SORTABLE color TAG SORTABLE size TAG SORTABLE
FT.AGGREGATE idx @color:{red|blue} GROUPBY 1 @category REDUCE TOLIST 1 @color AS color REDUCE TOLIST 1 @size AS size
Bug condition: if the search request @color:{red|blue}
returns no data for ToList reducer for size
field then lettusearch api return empty AggregateResults (but getCount() method return value > 0);
Expected behavior: return empty list for empty ToList reducer.
Redis cli response for above query:
1) (integer) 1
2) 1) "category"
2) "31"
3) "color"
4) 1) "red"
5) "size"
6) (empty list or set)
lettuSearchConnection.aggregate("idx", "@color:{red|blue}",
com.redislabs.lettusearch.AggregateOptions.builder()
.groupBy(List.of("category"), com.redislabs.lettusearch.AggregateOptions.Operation.GroupBy.Reducer
.ToList.builder().property("color").as("color").build(),
com.redislabs.lettusearch.AggregateOptions.Operation.GroupBy.Reducer
.ToList.builder().property("size").as("size").build()
).build()
).subscribe(ar -> {
long count = ar.getCount(); <<< count = 1
Map<String, Object> map = ar.get(0); <<< java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
});
Redis server 6.2.3
Redisearch 2.0.9
Lettusearch 3.1.2
Hi Team,
The getInfo method in RediSearchUtils does not work with RediSearch 2.0 module.
It throws the below exception.
Exception:java.lang.String cannot be cast to java.lang.Double
Thank you,
Does LettuSearch support connecting to self hosted redis cluster with RediSearch on cloud ?
with this commit : 02f72ad you removed the functionality of the K,V payload generics on the document. Any objections to changing the DocumentBuilder.fields and field() to take a K,V rather than String, String?
Hello, Im Aleksey
Im start use library and find interesting "bug"
When im try search like this (Im use kotlin)
val result = redis.rediSearchReactiveCommands.search(
indexName,
filters.joinToString(" "),
SearchOptions.builder()
.language(Language.Russian.name.toLowerCase())
.limit(Limit.builder()
.num(pageable.pageSize.toLong())
.offset(pageable.offset)
.build())
.returnFields(RETURN_CACHE_FIELDS)
.build())
.awaitFirst()
Filters contains search with Russian chars
Im use UTF-8 Codec for init
RediSearchClient.create(lettuceConnectionFactory.clientResources, redisURI)
.connect(StringCodec.UTF8!!, redisURI).reactive()
So when query go to RediSearch query string come "not valid" and redis return empty results
Im do some debug and find place in RediSearchCommandBuilder
method - public Command<K, V, SearchResults<K, V>> search(String index, String query, SearchOptions options)
code line with bug - RediSearchCommandArgs<K, V> commandArgs = createArgs(index);
commandArgs.add(query);
when call - commandArgs.add(query);
its just decode without CODEC which use in init and go to not valid for redisearch
I think this line must call - commandArgs.addKey(query);
Im do my custom copy paste code from this and its work fine!
Because commandArgs.addKey(query); decode value string with Codec!
Can you fix this or say why Russian query not correct work with this library?
Im can do PR for this or you fix it itself?)
Ty its very need for me
I try to perform multiget like so:
Mono<List<Map<String, String>>> res = conn.ftMget("my_index", "my_index:C6009e1aa52dea00b0f320c46da7a99a9", "my_index:C632be3ebf9bf590272f36e7e8b65d005");
Index is valid as are the doc ids. The exception is:
Type definition error: [collection type; class java.util.List, contains [map type; class java.util.Map, [simple type, class java.lang.String] -> [simple type, class java.lang.String]]]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Incompatible types: declared root type ([collection type; class java.util.List, contains [map type; class java.util.Map, [simple type, class java.lang.String] -> [simple type, class java.lang.String]]]) vs java.util.LinkedHashMap
It looks like a deserialization issue
Hi!
Is it possible to add support change default scorer in SearchOptions?
https://oss.redis.com/redisearch/Scoring/
SearchOptions without inKeys, inFields and returnFields results in Nullpointer exception.
if (!this.inKeys.isEmpty()) , if (!this.inFields.isEmpty()) and if (!this.returnFields.isEmpty()) in SearchOptions.java file will result nullpointer exception if fields are null.
Fix:
if (this.inKeys != null && !this.inKeys.isEmpty())
if (this.inFields != null && !this.inFields.isEmpty())
if (this.returnFields != null && !this.returnFields.isEmpty())
In our project, we use search as follows:
val result = redis.searchReactiveCommands.search(
indexName,
filters.joinToString(" "),
SearchOptions.builder()
.language(RUSSIAN_LANG)
.limit(Limit.builder()
.num(pageable.pageSize.toLong())
.offset(pageable.offset)
.build())
.returnFields(RETURN_CACHE_FIELDS)
.build())
.awaitFirst()
result.count()
will be equal to pageable.pageSize
.
I am interested in how to get the number of documents in the resultset?
For example, a similar command in the console
127.0.0.1:6379> FT.SEARCH SAMPLE_INDEX "(@name|tagsLower:test*)" LIMIT 0 10
1) (integer) 1000
2) "2000"
3) 1) "name"
2) "Test"
3) "nameLower"
4) "test"
Resultset size is returned along with the search results - 1) (integer) 1000
How to get this parameter using the lettusearch library?
I got IllegalArgumentException when used sentinel.
Caused by: java.lang.IllegalArgumentException: hostname can't be null
at java.net.InetSocketAddress.checkHost(InetSocketAddress.java:149) ~[?:?]
at java.net.InetSocketAddress.createUnresolved(InetSocketAddress.java:254) ~[?:?]
at io.lettuce.core.resource.SocketAddressResolver.resolve(SocketAddressResolver.java:91) ~[lettuce-core-5.1.8.RELEASE.jar:?]
at io.lettuce.core.resource.SocketAddressResolver.resolve(SocketAddressResolver.java:71) ~[lettuce-core-5.1.8.RELEASE.jar:?]
at com.redislabs.lettusearch.RediSearchClient.lambda$getSocketAddress$6(RediSearchClient.java:229) ~[lettusearch-1.5.3.jar:?]
at reactor.core.publisher.MonoCallable.subscribe(MonoCallable.java:56) ~[reactor-core-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at reactor.core.publisher.MonoPeek.subscribe(MonoPeek.java:71) ~[reactor-core-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at reactor.core.publisher.MonoPeek.subscribe(MonoPeek.java:71) ~[reactor-core-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at reactor.core.publisher.MonoPeek.subscribe(MonoPeek.java:71) ~[reactor-core-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at reactor.core.publisher.Mono.subscribe(Mono.java:3852) ~[reactor-core-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at reactor.core.publisher.Mono.subscribeWith(Mono.java:3958) ~[reactor-core-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at reactor.core.publisher.Mono.subscribe(Mono.java:3846) ~[reactor-core-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at reactor.core.publisher.Mono.subscribe(Mono.java:3813) ~[reactor-core-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at reactor.core.publisher.Mono.subscribe(Mono.java:3785) ~[reactor-core-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at io.lettuce.core.AbstractRedisClient.initializeChannelAsync(AbstractRedisClient.java:290) ~[lettuce-core-5.1.8.RELEASE.jar:?]
at com.redislabs.lettusearch.RediSearchClient.connectStatefulAsync(RediSearchClient.java:177) ~[lettusearch-1.5.3.jar:?]
at com.redislabs.lettusearch.RediSearchClient.connectStandaloneAsync(RediSearchClient.java:134) ~[lettusearch-1.5.3.jar:?]
at com.redislabs.lettusearch.RediSearchClient.connect(RediSearchClient.java:93) ~[lettusearch-1.5.3.jar:?]
at com.redislabs.lettusearch.RediSearchClient.connect(RediSearchClient.java:86) ~[lettusearch-1.5.3.jar:?]
Transformation(Apply) for aggregation is not working.
Getting error as : Value was not found in result (not a hard error) while running the below expression.
"format(\"%s\", @field)"
SearchResult does not return score with withScores field set as true in SearchOptions
Let's implement support for RediSearch 2.0:
https://redislabs.com/blog/redisearch-2-0-hits-its-first-milestone/
There is no support for "SCORER" in search options.
SCORER {scorer} : If set, we will use a custom scoring function defined by the user
FT.SEARCH <index_name> SCORER <scorer_name>
It seems like there are many Strings created throughout the code that can be avoided
e.g. the following creates a new String even when log level is not set to Debug
I was looking to get the search query highlighted in the resulting documents
Add support for LOAD * which loads all fields on FT.AGGREGATE whit and AggregateOptions.builder().loads()
For Chinese support in Redisearch, we need to add document to index by specifying language as Chinese. But did not found any option to specify the language.
Hi,
Would like to have support for GEOFILTER while doing search.
Thanks,
Giri
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.