georgekaraszi / activerecordextended Goto Github PK
View Code? Open in Web Editor NEWAdds additional postgres functionality to an ActiveRecord / Rails application
License: MIT License
Adds additional postgres functionality to an ActiveRecord / Rails application
License: MIT License
Ex:
cte = { "user_names(name)" => "values('jimmy'),('tommy'),('gummy')" }
User.with(cte).joins("JOIN user_names ON users.name = user_names.name").order(:name).to_a
ActiveRecord -- User Load -- { :sql => "WITH \"user_names(name)\" AS (values('jimmy'),('tommy'),('gummy')) SELECT \"users\".* FROM \"users\" JOIN user_names ON users.name = user_names.name ORDER BY \"users\".\"name\" ASC", :allocations => 909, :cached => nil }
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: relation "user_names" does not exist
LINE 1: ...y'),('gummy')) SELECT "users".* FROM "users" JOIN user_names...
^
Generated SQL:
User.with(cte).joins("JOIN user_names ON users.name = user_names.name").order(:name).to_sql
Actual SQL - Errored
"WITH \"user_names(name)\" AS (values('jimmy'),('tommy'),('gummy')) SELECT \"users\".* FROM \"users\" JOIN user_names ON users.name = user_names.name ORDER BY \"users\".\"name\" ASC"
Expected SQL
"WITH user_names(name) AS (values('jimmy'),('tommy'),('gummy')) SELECT \"users\".* FROM \"users\" JOIN user_names ON users.name = user_names.name ORDER BY \"users\".\"name\" ASC"
Observation:
As the cte name is quoted, it raise PG::UndefinedTable
Fix:
Try to avoid quoting when cte name/key is of such format, one of the way is to look for (
and )
inside the key.
I have to create a query like this below
WITH cte AS (
SELECT
"items"."title",
"items"."parent_code",
"items"."spend", (ROW_NUMBER() OVER number_window) AS "rank"
FROM "items"
WHERE "items"."profile_id" = 34
WINDOW number_window AS (PARTITION BY profile_id ORDER BY spend DESC)
)
SELECT title, parent_code
FROM cte
WHERE rank = 1;
I can write the query inside the WITH
parents = Item.define_window(:number_window) .partition_by(:profile_id, order_by: {spend: :desc}) .select(:title, :parent_code, :spend) .select_window(:row_number, over: :number_window, as: :rank) .where(profile_id: profile_ids)
but how to write the rest ?
SELECT title, parent_code
FROM cte
WHERE rank = 1;
If I try with
Item.with(parents: parents).where(rank: 1)
I get
SELECT * FROM items WHERE items.rank=1
instead I want to SELECT FROM the parents table (the CTE table)
Just upgrade to 6.1 and got these two warnings:
/Users/fny/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/active_record_extended-2.0.0/lib/active_record_extended/arel/nodes.rb:14: warning: already initialized constant Arel::Nodes::Contains
/Users/fny/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/activerecord-6.1.0/lib/arel/nodes/infix_operation.rb:50: warning: previous definition of Contains was here
It seems like Contains should be dropped if Rails.version > '6.1.0'
When calling .where.inet_contains
on a namespaced model, an exception is raised:
My::Namespaced::Model.where.inet_contains(cidr: '127.0.0.1')
# uninitialized constant MyNamespacedModel (NameError)
The following line tries to determine record class from table name:
https://www.postgresql.org/docs/current/ltree.html
This module implements a data type ltree for representing labels of data stored in a hierarchical tree-like structure. Extensive facilities for searching through label trees are provided.
A label is a sequence of alphanumeric characters, underscores, and hyphens. Valid alphanumeric character ranges are dependent on the database locale. For example, in C locale, the characters A-Za-z0-9_- are allowed. Labels must be no more than 1000 characters long.
Examples: 42, Personal_Services
A label path is a sequence of zero or more labels separated by dots, for example L1.L2.L3, representing a path from the root of a hierarchical tree to a particular node. The length of a label path cannot exceed 65535 labels.
Example: Top.Countries.Europe.Russia
and see https://patshaughnessy.net/2017/12/13/saving-a-tree-in-postgres-using-ltree
perhaps adapt https://github.com/cfabianski/ltree_hierarchy
Neat project you've got here! I found it through a post on Reddit.
I'm curious if there's been any consideration to add support for the PostgreSQL OVERLAPS
operator, as outlined in Date/Time Functions and Operators? If interested, I'd be happy to discuss and potentially open a pull request.
Cheers!
The example below doesn't seem to work. Is this by design? If so, could you please explain why?
scope :chain_members, -> (starting_node) do
baseline_term = Node.where(id: starting_node.id)
recursive_term = Node.joins('JOIN ancestors ON ancestors.parent_id = nodes.id')
recursive_cte = Node.with.recursive(ancestors: Node.union(baseline_term, recursive_term))
merge(recursive_cte).joins("INNER JOIN ancestors ON ancestors.id = member.node_id")
end
I have followed the installation step as below.
Add this line to your application's Gemfile:
gem 'active_record_extended'
And then execute:
$ bundle
Why I'm still getting the below issue?
irb(main):002:0> Developer.with
Traceback (most recent call last):
1: from (irb):2
NoMethodError (undefined method `with' for Developer (call 'Developer.connection' to establish a connection):Class)
irb(main):003:0>
Rails version: gem 'rails', '~> 5.1.4'
Ruby version: ruby-2.5.0
Should I have to require any module manually in Models?
I get the following error when trying to use union_all
as the source for a recursive CTE:
Source-ish:
base = Model1.where(id: id)
recursive = Model2.joins('INNER JOIN folders ON folders.id = model2.folder_id')
with.recursive(folders: union_all(base, recursive)).joins('INNER JOIN folders on folders.id = model3.folder_id')
Error:
ActiveRecord::StatementInvalid: PG::InvalidRecursion: ERROR: recursive query \
"folders" does not have the form non-recursive-term UNION [ALL] recursive-term
With the resulting SQL:
WITH RECURSIVE "folders" AS (
SELECT "model3".* FROM (((
SELECT "model1".*
FROM "model1"
WHERE "model1"."id" = '12345')
UNION ALL (
SELECT "model2".*
FROM "model2"
INNER JOIN folders ON folders.id = model2.folder_id
))) model3
)
SELECT "model3".*
FROM "model3"
INNER JOIN folders on folders.id = model3.folder_id
I think the problem is the select that is surrounding the unioned queries.
Unions are not preserved when merging. Please see testcase below.
To give more context: this can have critical consequences. For example, when using unions in scopes which end up in CanCanCan rules it leads to all records being accessible, irrespective of the scope, as CanCanCan merges them when calculating the accessible records.
I see that there's code in relation_patch.rb but it seems that it's no longer to working. For example, I cannot find the method normal_values
in Rails (anymore?); instead there is a constant NORMAL_VALUES
. As a quick-n-dirty try I added :unionize
to this constant, but leads to an error due to the method unionize!
not existing.
diff --git i/spec/query_methods/unionize_spec.rb w/spec/query_methods/unionize_spec.rb
index bbd77fb..ff0879e 100644
--- i/spec/query_methods/unionize_spec.rb
+++ w/spec/query_methods/unionize_spec.rb
@@ -49,6 +49,14 @@ RSpec.describe "Active Record Union Methods" do
query = User.union(User.select(:id), User.select(:id))
expect(query.pluck(:id)).to have_attributes(size: expected_ids.size).and(match_array(expected_ids))
end
+
+ context "when merging in query" do
+ it "will maintain the union table when merging into existing AR queries" do
+ other = User.union(User.where(id: user_one.id), User.joins(:profile_l).where.not(id: user_one.id))
+ query = User.merge(other)
+ expect(query).to match_array(other.to_a)
+ end
+ end
end
describe ".union.all" do
Sometimes when preceding model scopes change in ways that seem like they shouldn't matter some of the methods in the gem begin to fail and produce odd side effects. Removing the chaining seems to make them work again, even if the same association that was calling in the method chain is just converted to an argument.
For example the below started happening when a change was made to the preceding scope (introducing strictest_dates scope to replace between_dates, both shown at the very bottom to reduce clutter), which suddenly made the union_except stop returning the correct new association and also has side effects on preceding associations chained with it
class Match < ApplicationRecord
#...
scope :had_circle, -> (circle_phase) { left_joins(:circles).where('circles.phase = ?', circle_phase) }
scope :missing_circle, -> (circle_phase) { union_except(all, had_circle(circle_phase)) }
scope :test_missing_circle, -> (existing_assoc, circle_phase) { union_except(existing_assoc, had_circle(circle_phase)) }
#...
end
irb(main):008> q1 = Match.strictest_dates(m_from, m_to, minor_version_start,minor_version_end)
=> [#<Match:0x00007cade8abe420
irb(main):009> q1.count
=> 4478
irb(main):010> q2 = Match.test_missing_circle(q1, missing_circle+1)
=> [#<Match:0x00007cade8a13f20
irb(main):011> [q1.count, q2.count]
=> [4478, 4478]
irb(main):017> q3 = q1.had_circle(7)
=>
[#<Match:0x00007cae018cbcd0
irb(main):018> [q1.count, q2.count, q3.count]
=> [4478, 4478, 2638]
irb(main):019> q4 = q1.missing_circle(missing_circle+1)
=> [0, 4478, 0, 0]
The change triggered the unexpected behaviour was replacing between_dates
in the calling function with strictest_dates
shown below. I've had similar misbehaviour before with some other scopes using any_of
but didn't investigate and just factored them out. All of the below is on PostgreSQL 14.11 (Ubuntu 14.11-0ubuntu0.22.04.1) and Rails 7.0.8:
class Match < ApplicationRecord
scope :after_date, ->(start_date) { where(creation_time: start_date.end_of_day..) }
scope :before_date, ->(end_date) { where(creation_time: ..end_date.beginning_of_day) }
scope :between_dates, ->(start_date, end_date) { where(creation_time: start_date.end_of_day..end_date.beginning_of_day) }
scope :inside_version, ->(version_number) { where(creation_time: convert_pc_version_to_exclusive_date_range(version_number) ) }
scope :between_versions, ->(start_version, end_version) {
where(creation_time: convert_pc_version_to_exclusive_date_range(start_version).first..convert_pc_version_to_exclusive_date_range(end_version).last )
}
scope :strictest_dates, ->(start_date, end_date, start_version=nil, end_version=nil) {
if start_version && end_version
matches = union_intersect(between_dates(start_date, end_date), between_versions(start_version, end_version))
else
matches = between_dates(start_date, end_date)
end
matches
}
i saw the pr #47 fixed the problem, but ci fail.
Let's say I want to get records where the services
column does not include the string 'free_ice_cream'
.
With plain Active Record, i'd do something like Party.where.not("'free_ice_cream' = ANY (services)")
.
Is there a way to do this with active_record_extended
?
To find Parties with ice cream, i can easily query for Party.where.any(services: 'free_ice_cream')
,
but something like Party.where.any.not(...)
throws
ArgumentError: wrong number of arguments (given 0, expected 1+) active_record_extended/query_methods/where_chain.rb:18:in `any'
I'm using Rails 6.0.
Thanks in advance :)
Can use this with simple form for like
= f.input :log_cardholderName_jcont, label:false, required:false, placeholder:'Log', type: 'string'
where cardholderName is key in jsonb of log column
OR
= f.input :log_jcont, label:false, required:false, placeholder:'Log', type: 'string'
Hi,
All the examples I could see are for the same table. I've tried messing around to see if I could get the UNION of multiple tables but failed every time. I'm basically wanting to produce a query that looks something like:
SELECT id, updated_at FROM table_1 UNION SELECT id, updated_at FROM table_2 ORDER BY updated_at;
If I try something like this I only get results from table_1:
t1 = Table1.select(:id, :updated_at).where('updated_at > ?', 2.weeks.ago)
t2 = Table2.select(:id, :updated_at).where('updated_at > ?', 2.weeks.ago)
results = Table1.union(t1).union(t2)
I am not sure if there is something wrong with my setup or if there is an issue with the gem. It seems that I cannot use the contains
query against JSONB fields that contain an array of objects. It returns an error trying to parse the parameters. The raw Postgres query works fine.
Raw PostgresSQL query
Button.where("draft_button_fields @> ?", [{required: "always"}].to_json).count
Button Count (1.7ms) SELECT COUNT(*) FROM "buttons" WHERE (draft_button_fields @> '[{"required":"always"}]') /*line:(pry):2:in `__pry__'*/
=> 1
It errors with or without the array.
Error:
Button.where.contains(draft_button_fields: [{required: "always"}]).to_sql
TypeError: can't quote Hash
from /Users/avanrielly/.asdf/installs/ruby/3.1.4/lib/ruby/gems/3.1.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/quoting.rb:25:in `quote'
Table structure:
Table "public.buttons"
Column | Type | Collation | Nullable | Default
------------------------+--------------------------------+-----------+----------+-------------
id | bytea | | not null |
...
draft_button_fields | jsonb | | | '[]'::jsonb
Column data:
[{"field_id": "name", "required": "always", ...}]
Hi,
Many thanks for your work!
I have one question:
Is it possible to upgrade pg gem dependency to the latest version 1.1.4?
I have a case where I need to be able to specify the MATERIALIZED
option on a CTE as documented here https://www.postgresql.org/docs/current/queries-with.html#id-1.5.6.12.7
To take the example from the readme
User.with(highly_liked: ProfileL.where("likes > 300"))
.joins("JOIN highly_liked ON highly_liked.user_id = users.id") #=> [bob, randy]
Generates
WITH "highly_liked" AS (SELECT "profile_ls".* FROM "profile_ls" WHERE (likes >= 300))
SELECT "users".*
FROM "users"
JOIN highly_liked ON highly_liked.user_id = users.id
But I need a way for it to generate
WITH "highly_liked" AS MATERIALIZED (SELECT "profile_ls".* FROM "profile_ls" WHERE (likes >= 300))
SELECT "users".*
FROM "users"
JOIN highly_liked ON highly_liked.user_id = users.id
Is this something that could be added?
This library looks great, but I'm puzzled by the inclusion of the ar_outer_joins
library as a dependency considering it hasn't had a release in 8 years. Looks like it is only used by either_join
and either_order
.
Is anyone else concerned by that, or not?
Rails added Arel::Nodes::Contains
back in version 6.1 (see rails/rails@3a8eee9)
The visitor in this library breaks the native implementation when the table name does not exactly map to the model class:
Specifically the constantize
fails with NameError: uninitialized constant <Thing>
.
You run into this if you namespace your models (Models::Thing
) or you manually set self.table_name=
(and you are using the native contains node in some fashion e.g arel_table[column_name].contains(...)
)
This library avoids the issue by making a ContainsHStore
directly in the where_chain builder:
There seem to be two potential solutions. The current implementation of visit_Arel_Nodes_Contains
has to proxy between the various containment subtypes, so:
a) remove this visitor all-together by moving the proxy logic earlier (ala where_chain) and not using Arel::Nodes::Contains
at all in this library
b) rewrite the column type lookup logic to handle irregular table names gracefully
I have added
gem 'active_record_extended'
then bundle
but I get this error
irb(main):005:0> Profile.with(test: Profile.all)
Traceback (most recent call last):
1: from (irb):5
NoMethodError (undefined method `with' for #<Class:0x00007fab63c67c88>)
irb(main):007:0> Profile.public_methods.include?(:with)
=> false
What could be the reason?
Gemfile.lock
active_record_extended (1.4.0)
activerecord (>= 5.0, < 6.1)
ar_outer_joins (~> 0.2)
pg (< 2.0)
config/boot.rb
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile.
require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
config/application.rb
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
# CUSTOM
require "active_record_extended"
config/enviroment.rb
# Load the Rails application.
require_relative 'application'
# Initialize the Rails application.
Rails.application.initialize!
.rvm/gems/ruby-2.7.2/gems/active_record_extended-2.0.0/lib/active_record_extended/arel/nodes.rb:14: warning: already initialized constant Arel::Nodes::Contains
.rvm/gems/ruby-2.7.2/gems/activerecord-6.1.1/lib/arel/nodes/infix_operation.rb:50: warning: previous definition of Contains was here
I has a quick look over ARE, and it looks pretty good. To be honest, I'm eager to give it a try.
My only concern, currently, is reimplementing/overwriting the regular AR .all
method. What's the reasoning behind it?
gem version: v2.0.0
Using Post.union
only works on the development environment but raises an error in the test environment.
Post.all.union
works in both environments.
Is it the intended behavior? because I can't found the documentation about it in Readme.
Any help would be appreciated. Thanks~
Thanks for the library.
The following code results in the execution of the two queries specified below.
def set_post
owned_posts = current_user.posts
shared_posts = current_user.shared_posts
@post =
Post
.union(owned_posts, shared_posts)
.find_by(id: params[:id])
end
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = $1 [["user_id", "91b027c3-3d08-4520-a6ee-a7498c90bcff"]]
SELECT "posts".* FROM (( (SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = '91b027c3-3d08-4520-a6ee-a7498c90bcff') UNION (SELECT "posts".* FROM "posts" INNER JOIN "collaborations" ON "posts"."id" = "collaborations"."post_id" WHERE "collaborations"."user_id" = '91b027c3-3d08-4520-a6ee-a7498c90bcff') )) posts WHERE "posts"."id" = $1 LIMIT $2 [["id", "6a3df22b-082e-4d83-87d0-b5c92fc3cc87"], ["LIMIT", 1]]
It appears that whenever I pass one of the two objects (owned_posts or shared_posts) into the first argument of the union
method, it'll execute that query prior to executing the proper UNION query.
I found a way around it by converting the queries to SQL strings before to passing them to the union
method:
def set_post
owned_posts = current_user.posts.to_sql # necessary to avoid additional query
shared_posts = current_user.shared_posts.to_sql # unnecessary, but works
@post =
Post
.union(owned_posts, shared_posts)
.find_by(id: params[:id])
end
This results in just a single query:
SELECT "posts".* FROM (( (SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = '4a295157-2786-4508-bc6c-cb9aecaafd8b') UNION (SELECT "posts".* FROM "posts" INNER JOIN "collaborations" ON "posts"."id" = "collaborations"."post_id" WHERE "collaborations"."user_id" = '4a295157-2786-4508-bc6c-cb9aecaafd8b') )) posts WHERE "posts"."id" = $1 LIMIT $2 [["id", "6a3df22b-082e-4d83-87d0-b5c92fc3cc87"], ["LIMIT", 1]]
Is there a reason why I can't just pass in current_user.posts
instead of current_user.posts.to_sql
? Is it acceptable to call .to_sql
prior to passing it into the union
method?
Thanks!
I have a model User
with an array column "notifications", and when I try to query it like this:
User.where.any(notifications: "foo")
the generated SQL is
SELECT "users".* FROM "users" WHERE NULL = ANY("users"."id")
If I use any other column name, it works fine:
User.where.any(whatever: "foo")
generates
SELECT "users".* FROM "users" WHERE 'foo' = ANY("users"."whatever")
Is there something special about the column name "notifications"? ๐ค
Hello! I've found a bug with the .with
CTE functionality.
When a relation has a CTE added, and that relation attempts to use .with
to add another CTE of itself, it enters an infinite loop that results in a SystemStackError
.
# Note: The models don't matter here. I used some that are present in the spec/support/models.rb file for easy reproduction.
# Initial Relation
initial_relation = User.all
# Add CTE to User Relation
cte_relation = Group.all
initial_relation_with_cte = initial_relation.with('first_cte' => cte_relation)
# When a relation with a CTE adds itself as another CTE a SystemStackError is raised
initial_relation_with_self_cte = initial_relation_with_cte.with('self_cte' => initial_relation_with_cte)
initial_relation_with_self_cte.to_sql # .arel also triggers the loop
I've spent a couple of days looking into this. Here is the infinite loop.
build_arel
method is called by rails, and since the relation has a CTE, build_with
is called.build_with
method calls generate_grouping
which calls to_arel_sql
.to_arel_sql
calls .to_sql
in the ActiveRecord::Relation
branch..to_sql
of an ActiveRecord::Relation
calls build_arel
again in step 1.If I .dup
the relation and the associated .cte
, the relation behaves as expected.
# Initial Relation
initial_relation = User.all
# Add CTE to User Relation
cte_relation = Group.all
initial_relation_with_cte = initial_relation.with('first_cte' => cte_relation)
# duplicate the relation and the cte
self_cte = initial_relation_with_cte.dup
self_cte.cte = initial_relation_with_cte.cte.dup
# use duplicated relation
initial_relation_with_self_cte = initial_relation_with_cte.with('self_cte' => self_cte)
initial_relation_with_self_cte.to_sql # works as expected
In the readme file it shows:
user_1 = Person.where(id: 1)
user_2 = Person.where(id: 2)
users = Person.where(id: 1..3)
Person.union(user_1, user_2, users) #=> [#<Person id: 1, ..>, #<Person id: 2,..>, #<Person id: 3,..>]
# You can also chain union's
Person.union(user_1).union(user_2).union(users)
But when using the union method in rails, it will throw an error NoMethodError: undefined method 'union' for #<Class:0x00007fcf10868468>
To get this to work, with the example from the readme, I would need to call it as Person.all.union(user_1, user_2, users)
The readme should be updated to reflect this.
Forgive me for being a bit out of my element here. I'm coming back to Rails after a decade and haven't had to handle complex SQL in longer than that.
I'm trying to work the Manager's Tree sort of problem using the block of code below. This specific example should return 11 records, and the UUID is the root level manager. I have 102 Group records in this database, which are all returned by this query. So, my query seems to be the equivalent of Group.all
.
group = Group.where(id: '7918c477-8843-471a-bfcd-e47982fed0c6')
recursive = Group.joins('JOIN gtree ON gtree.id = groups.parent_id')
Group.with.recursive(gtree: Group.union_all(group, recursive).to_union_sql)
A dump of the SQL:
WITH RECURSIVE "gtree" AS ((
(SELECT "groups".*
FROM "groups"
WHERE "groups"."id" = '7918c477-8843-471a-bfcd-e47982fed0c6')
UNION ALL
(SELECT "groups".*
FROM "groups"
JOIN gtree ON gtree.id = groups.parent_id)
))
SELECT "groups".* FROM "groups"
When I look at a SQL exemplar (Real World Examples section for getting the manager tree), they look virtually identical. Which leads me to wonder what I'm doing wrong.
Suggestions?
Edit: At the parent level, parent_id is null. Any group not belonging to a network has parent_id null. But, when I add not null, there's no change.
group = Group.where(id: '7918c477-8843-471a-bfcd-e47982fed0c6').where.not(parent_id: nil)
recursive = Group.joins('JOIN gtree ON gtree.id = groups.parent_id')
Group.with.recursive(gtree: Group.union_all(group, recursive).to_union_sql)
WITH RECURSIVE "gtree" AS ((
(SELECT "groups".*
FROM "groups"
WHERE "groups"."id" = '7918c477-8843-471a-bfcd-e47982fed0c6'
AND "groups"."parent_id" IS NOT NULL)
UNION ALL
(SELECT "groups".* FROM "groups" JOIN gtree ON gtree.id = groups.parent_id)
))
SELECT "groups".* FROM "groups"
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.