Giter Site home page Giter Site logo

cakephp-elastic-search-datasource's Introduction

Build Status Coverage Status Total Downloads Latest Stable Version Documentation Status Gratipay

CakePHP Elastic Plugin & ElasticSearch "ElasticSource" Datasource

Conveniently index and access records in ElasticSearch.

Background

Seamlessly transition from MySQL or another SQL/DBO-backed data source into the amazing ElasticSearch NoSQL indexer. Conforms to CakePHP's ORM but also provides access to facets and queries.

Patches and issues welcome. Please include unit tests.

Requirements

Installation

[Manual]

[GIT Submodule]

In your app directory type:

git submodule add git://github.com/josegonzalez/cakephp-elastic-search-datasource.git Plugin/Elastic
git submodule update --init

[GIT Clone]

In your app directory type

git clone git://github.com/josegonzalez/cakephp-elastic-search-datasource.git Plugin/Elastic

Enable plugin

Enable the plugin your app/Config/bootstrap.php file:

CakePlugin::load('Elastic');

If you are already using CakePlugin::loadAll();, then this is not necessary.

Usage

Setup ElasticSearch

Setup an instance of ElasticSearch to use if you don't have one already.

Open Config/database.php and add a datasource called index:

public $index = array(
	'datasource' => 'Elastic.ElasticSource',
	'index' => 'people',
	'port' => 9200
);

And create your model:

class Contact extends AppModel {
	
	public $useDbConfig = 'index';
	
}

This will store your Contact model in the people index as the type contacts. By default the ElasticSearch "type" is the same as the table name you would use for your model. If you'd like to change the ElasticSearch type then add the variable useType to your model:

class Contact extends AppModel {
	
	public $useDbConfig = 'index';
	
	public $useType = 'mytype';
	
}

Map your model

Elastic Plugin comes with a shell to help you with managing indexes, creating mappings, and indexing records:

Davids-MacBook-Pro:app dkullmann$ Console/cake Elastic.elastic
ElasticSearch Plugin Console Commands. Map and index data

Usage:
cake elastic.elastic [subcommand] [-h] [-v] [-q]

Subcommands:

create_index  Create or alias an index
mapping       Map a model to ElasticSearch
index         Index a model into ElasticSearch
list_sources  Display output from listSources

To see help on a subcommand use `cake elastic.elastic [subcommand] --help`

Options:

--help, -h     Display this help.
--verbose, -v  Enable verbose output.
--quiet, -q    Enable quiet output.

To start, create your index

Console/cake Elastic.elastic create_index test

Case 1: Your model is already in the 'default' datasource

You can copy the schema with this command:

Console/cake Elastic.elastic mapping Contact

Case 2: Your model is in another datasource that responds to describe:

Console/cake Elastic.elastic mapping Contact -d <datasource>

Case 3: Your model is not yet mapped

You can add a method to your model called elasticMapping to generate the mapping.

Special ElasticSearch types such as geopoint and multi_field are supported.

class Contact extends AppModel {

	public $useDbConfig = 'index';

	public $_mapping = array(
		'id' => array('type' => 'integer'),
		'name' => array('type' => 'string'),
		'number' => array('type' => 'string'),
		'special_type' => array(
			'type' => 'multi_field',
			'fields' => array(
				'not_analyzed' => array('type' => 'string', 'index' => 'not_analyzed'),
				'analyzed' => array('type' => 'string', 'index' => 'analyzed')
			)
		),
		'created' => array('type' => 'datetime'),
		'modified' => array('type' => 'datetime')
	);

	public function elasticMapping() {
		return $this->_mapping;
	}
}

Index a few records

If you do not yet have data in MySQL and you are following along with this tutorial you should create your MySQL tables and data:

CREATE TABLE `contacts` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `number` varchar(255) DEFAULT NULL,
  `special_type` varchar(255) DEFAULT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `contacts` (`id`, `name`, `number`, `special_type`, `created`, `modified`)
VALUES
	(1, 'David', '555-888-1212', 'Multifield', '2012-07-19 20:31:29', '2012-07-19 20:31:29');

Otherwise simple start indexing.

To start indexing records you can use this command:

Console/cake Elastic.elastic index Contact

The ElasticShell will add the IndexableBehavior to your model if it's not already added. To add it permanently add it in your model:

public $actsAs = array('Elastic.Indexable');

By default IndexableBehavior will declare your "modified" field as the field which tracks when each record was updated or created in order to synchronize it with ElasticSearch.

Test using this command

curl -XGET 'http://localhost:9200/people/contacts/1

The output should include:

{
  "_index" : "test",
  "_type" : "contacts",
  "_id" : "1",
  "_version" : 2,
  "exists" : true, "_source" : {"Contact":{"created":"2012-07-19 20:31:29","id":"1","modified":"2012-07-19 20:31:29","name":"David","number":"555-888-1212","special_type":"Multifield"}}
}

CRUD Operations

Because ElasticSource conforms to the CakePHP ORM CRUD operations are easy:

// Create
$record = array(
	'id' => 1,
	'name' => 'David',
);

$this->Model->create($data);
$this->Model->save();

// Read
$this->Model->findById(1);

// Update
$this->Model->save($data);

// Delete
$this->Model->delete(1);

Querying

Querying with Elastic Search using this plugin is very simple and you will find it is extremely similar to what you are already used to for querying relataional databases:

Simple conditions

Use the Model.field notation followed by an operator:

<?php
$this->Model->find('all', array(
	'conditions' => array(
		'Model.name' => 'text', // produces {term: {"Model.name": "text"}}
		'Model.quantity >' => 10 // produces {range: "Model.quantity": {gt: 10}}, can also use <, <=, >=
	)
));

Distance based geo location queries

The following query will search all results located at most 10 miles from provided lat and long:

<?php
$this->Model->find('all', array(
	'conditions' => array(
		'Model.location' => array(
			'distance' => '10mi',
			'lat' => 10,
			'lon' => 70
			'unit' => 'mi' //Optional (also accepts km)
			'distance_type' => 'arc' // Optional, defaults to plain
		)
	)
));

Boolean Queries

Boolean queries are a very powerful tool to filter results based on conditions that should be met strictly and others that are just hints or "nice to have". Bool queries are created in the following way:

<?php
$this->Model->find('all', array(
	'conditions' => array(
		'bool' => array(
			array('Model.tags should' => 'cakephp'),

			// Multiple conditions per field
			array('Model.tags must' => 'cakephp'),
			array('Model.tags must' => 'elastic'),
			array('Model.tags must' => 'source'),

			'Model.readers must_not <' => 100,
		)
	)
));

Query String Searches

Should you need searching on multiple fields at the same time using a single query string, then use the query_string key:

<?php
$this->Model->find('all', array(
	'conditions' => array(
		'query_string' => array(
			//Search on both name and description using a boost for name
			'fields' => array('Model.name^2', 'Model.description'),
			'query' => 'Text to be looked up'
		)
	)
));

has_parent / has_child Queries

If you are writing a has_parent or has_child query, you need to replace 'type' from the standard JSON query with 'model':

<?php
$this->Model->find('all', array(
	'conditions' => array(
		'has_child' => array(
			//Maps to Elasticsearch 'type'
			'model' => 'ChildModel',
			'child_model_id' => $id
		)
	)
));

Additional

Advanced features available using the IndexableBehavior such as bound and polygonal geo location searching and sorting

Todo

  • Have more people use it and tell me what they want / patch it
  • Unit tests!
  • Re-arrange some logic to conform to the ORM in a more simple / less hacky fashion
  • Document IndexableBehavior

License

Copyright (c) 2012 David Kullmann

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

cakephp-elastic-search-datasource's People

Contributors

criztianix avatar derekperkins avatar dkullmann avatar el-barto avatar jippi avatar josegonzalez avatar lorenzo avatar mariano avatar reado avatar ryantology avatar sarce avatar zeroasterisk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cakephp-elastic-search-datasource's Issues

Undefined property: ElasticSource::$columns

I'm getting these warnings most times that I'm saving a record through the data source, but not every time. I don't know if I'm doing something incorrectly or how to track down these warnings. Is this common?

Notice (8): Undefined property: ElasticSource::$columns [APP/Vendor/pear-pear.cakephp.org/CakePHP/Cake/Model/Model.php, line 1749]
Warning (2): array_merge() [function.array-merge]: Argument #2 is not an array [APP/Vendor/pear-pear.cakephp.org/CakePHP/Cake/Model/Model.php, line 1749]
Warning (2): array_key_exists() expects parameter 2 to be array, null given [APP/Vendor/pear-pear.cakephp.org/CakePHP/Cake/Model/Model.php, line 1750]

Boolean with range queries fail

I ran into some issues with boolean matches against fileds mapped as range (integers in particular).

This is a temp fix for this
https://github.com/ryantology/CakePHP-Elastic-Search-DataSource/commit/3badc2d14e65d6334106696529c320c9703e0925

The real issue is that the coupling of boolean searches and the range operators should be separated in some way. Not sure if you guys have run into the same issue.

The patch allows this query to work:

'conditions' => array(
    'bool' => array(
        'user_id must' => 2134,
        'content_type must_not' => 342
    )
)

Using count returns an error from ElasticSearch

I'm trying to count documents from a type called ix_resources in my index called my_index. Here's a sample code:

        var_dump($this->IxResource->find('count'));

        var_dump($this->IxResource->find('count', array(
            'conditions' => array(
                'IxResource.resource_type' => 2,
            )
        )));

In both cases the printed result is 0, which is wrong. I did a little debugging of ElasticSearch's response, and I see that while the value of _count in the result is zero, I'm getting error messages likes these from every shard:

For the first query:
BroadcastShardOperationFailedException[[my_index][0] ]; nested: QueryParsingException[[my_index] request does not support [match_all]];

For the second one:
BroadcastShardOperationFailedException[[my_index][0] ]; nested: QueryParsingException[[my_index] request does not support [filtered]];

I believe it has to do with the way the query needs to be constructed for the Count API.

I think there are two things to improve here:
a) The way the count queries are built
b) Error handling, because no exception was raised in my tests.

Model not in "default" datasource

I have my models attached to "mydb" and not in "default" datasource.

I can't use

public $useDbConfig = 'index';

in my model because I still use it for my connection.

How do it?

php strict warnings

Strict (2048): Declaration of ElasticSource::listSources() should be compatible with DataSource::listSources($data = NULL) [APP\Plugin\Elastic\Model\Datasource\ElasticSource.php, line 1705]

Strict (2048): Declaration of ElasticSource::describe() should be compatible with DataSource::describe($model) [APP\Plugin\Elastic\Model\Datasource\ElasticSource.php, line 1705]

Strict (2048): Declaration of ElasticSource::read() should be compatible with DataSource::read(Model $model, $queryData = Array, $recursive = NULL) [APP\Plugin\Elastic\Model\Datasource\ElasticSource.php, line 1705]

Strict (2048): Declaration of ElasticSource::update() should be compatible with DataSource::update(Model $model, $fields = NULL, $values = NULL, $conditions = NULL) [APP\Plugin\Elastic\Model\Datasource\ElasticSource.php, line 1705]

Conditions not combining correctly... query bypass

I am unable to mix and match conditions in any way that works...

Document in the index

    {"took":1,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":0.2936909,"hits":[{"_index":"ad","_type":"esindex","_id":"ITbvwWAwT9mlfV3oT3G0YA","_score":0.2936909, "_source" : 
            {"ElasticSearchIndex":{"model":"User","association_key":"15340000-0000-0000-0000-000000000007","data":"15340000-0000-0000-0000-000000000007 [email protected] Alan Blount AudiologyHoldings.com 502.744.4942","created":"2014-01-07 23:40:57","modified":"2014-01-07 23:40:57"}}
            }]}}

Non-Working Attempts

    $conditions = array(
        'ElasticSearchIndex.model' => $Model->alias,
        'ElasticSearchIndex.data' => $q,
    );
    // {"query":{"filtered":{"query":{"match_all":{}},"filter":{"and":[{"term":{"ElasticSearchIndex.model":"User"}},{"term":{"ElasticSearchIndex.data":"alan"}}]}}}}
    // {"took":1,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}
    $conditions = array(
        'bool' => array(
            'ElasticSearchIndex.model must' => $Model->alias,
            'ElasticSearchIndex.data must' => $q,
        ),
    );
    // {"query":{"filtered":{"query":{"match_all":{}},"filter":{"bool":{"must":[{"term":{"ElasticSearchIndex.model":"User"}},{"term":{"ElasticSearchIndex.data":"alan"}}]}}}}}
    // {"took":1,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}
    $conditions = array(
        'bool' => array(
            'ElasticSearchIndex.model must' => $Model->alias,
        ),
        'query_string' => array(
            'query' => $q,
        ),
    );
    // {"query":{"filtered":{"query":{"match_all":{}},"filter":{"and":[{"bool":{"must":[{"term":{"ElasticSearchIndex.model":"User"}}]}},{"query":{"query_string":{"query":"alan"}}}]}}}}'
    // {"took":1,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}

Working Bypass

I have put the array of direct query data into the find options (look for a pull request for this).

    $findOptions['query'] = array(
        "filtered" => array(
            "query" => array(
                "bool" => array(
                    "must" => array(
                        array(
                            "match" => array(
                                "ElasticSearchIndex.model" => $Model->alias,
                            )
                        ),
                        array(
                            "match" => array(
                                "ElasticSearchIndex.data" => $q,
                            )
                        ),
                    ),
                ),
            ),
        ),
    );
    // {"query":{"filtered":{"query":{"filtered":{"query":{"bool":{"must":[{"match":{"ElasticSearchIndex.model":"User"}},{"match":{"ElasticSearchIndex.data":"alan"}}]}}}}}}}

While I can get the results I need with this, it's a total bypass of what should be going on.


Am I doing something wrong?

Any way we can split these functions up and make them easier to test & understand?

How to use _id?

First, thanks for this plugin. I believe if I can solve this issue it might be very useful for me.

I need to save a document to ES and then update it later. This is usually done by referencing _id. I would have expected this to do just that:

$arr=array('id'=>1,'name'=>'shannon');
$this->Babes->create($arr);
$this->Babes->save();

This does in fact succesfully index the record to ES but it generates a random (ES-style) _id for the document, such as AU2Cy2shXX7jfslMLArv. So it would be impossible to lateron call ->save($arr) to update the record. I've also tried adding _id to the array.

What am I missing here?

cannot connect the elasticsearch in cakephp Do i missing something

Missing Database Table

Error: Database table contacts for model Contact was not found.

Notice: If you want to customize this error message, create app/View/Errors/missing_table.ctp

Stack Trace
#0 /var/www/efashion/lib/Cake/Model/Model.php(3258): Model->setSource('contacts')
#1 /var/www/efashion/lib/Cake/Model/Model.php(2483): Model->getDataSource()
#2 /var/www/efashion/app/Controller/ContactsController.php(9): Model->find('all')
#3 [internal function]: ContactsController->index()
#4 /var/www/efashion/lib/Cake/Controller/Controller.php(473): ReflectionMethod->invokeArgs(Object(ContactsController), Array)
#5 /var/www/efashion/lib/Cake/Routing/Dispatcher.php(104): Controller->invokeAction(Object(CakeRequest))
#6 /var/www/efashion/lib/Cake/Routing/Dispatcher.php(86): Dispatcher->_invoke(Object(ContactsController), Object(CakeRequest), Object(CakeResponse))
#7 /var/www/efashion/app/webroot/index.php(96): Dispatcher->dispatch(Object(CakeRequest), Object(CakeResponse))
#8 {main}

Can't get 'order' to work

Hi,

First of all, thanks for this plugin. I'm finding it very useful :)

Now, to my problem: I'm trying to do a very basic query with an order clause. Here's the code for the action of my controller:

public function batch() {
        $resources = $this->Resource->find('all', array(
            'conditions' => array(
                'visible' => true,
                'deleted' => false,
            ),
            'limit' => self::BATCH_LIMIT,
            'page' => (int)$this->request->data('batch'),
            'order' => array('created' => 'desc'),
        ));

        $this->autoRender = false;
        $this->response->type('json');
        $this->response->body(json_encode($resources));
}

I have two problems with that:

  1. The sorting doesn't appear to be working. I get the same payload with or without the 'order' key on the query.
  2. json_decode throws a notice which breaks up my output. This only happens if I add the 'order' key.
    Notice (8): json_decode(): integer overflow detected [APP/Plugin/Elastic/Model/Datasource/ElasticSource.php, line 1472]

I'm having troubles debugging this. Can you help me figure out if I'm doing something wrong?

Direct JSON Query similar to Model::query

I have a complex query (see below) that works using cURL, but I don't know how to form the syntax so that the datasource can interpret it.

{
  "query": {
    "has_child": {
      "type": "workspaces_posts_tags",
      "query" : {
        "filtered": {
          "query": { "match_all": {}},
          "filter" : {
            "and": [
              {"term": {"workspace_id": 2}},
              {"term": {"post_id": 4}}
            ]
          }
        }
      }
    }
  }
}

I tried setting $query to this in the beforeFind in a custom find method, but it didn't like it. It also didn't give any meaningful errors.

$query = [
    'conditions' => [
        'has_child' => [
            'type' => 'workspaces_posts_tags',
            'query' => [
                'filtered' => [
                    'query' => [
                        'match_all' => []
                    ],
                    "filter" => [
                        'and' => [
                            'term' => [
                                'workspace_id' => 2
                            ],
                            'term' => [
                                'post_id' => 4
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ]
];

My thoughts are that it might be nice to have a method available where I could pass in a JSON formatted query and use that as the query. Is there a way to do that? If not, do you know how I should format the query properly for this plugin?

Undefined variable: field - Unable to Index

Hi,
I'm not able to index any model using Elastic.elastic index
I keep getting this below error message.

Please advise
Thanks

Notice Error: Undefined variable: field in [/Projects/ecommerce/plugins/Elastic/Model/Datasource/ElasticSource.php, line 793]

Notice Error: Undefined variable: field in [/Projects/ecommerce/plugins/Elastic/Model/Datasource/ElasticSource.php, line 805]

Notice Error: Undefined variable: field in [/Projects/ecommerce/plugins/Elastic/Model/Datasource/ElasticSource.php, line 826]

Warning Error: Object of class stdClass could not be converted to string in [/Projects/ecommerce/plugins/Elastic/Model/Datasource/ElasticSource.php, line 1694]

Retrieving data from mysql starting on 1970-01-01 00:00:00
Warning Error: Object of class stdClass could not be converted to string in [/Projects/ecommerce/plugins/Elastic/Model/Datasource/ElasticSource.php, line 1694]

Error: ElasticSearch Error: Status: 400
#0 /Projects/ecommerce/plugins/Elastic/Model/Datasource/ElasticSource.php(1483): ElasticSource->_throwError(Object(stdClass))
#1 /Projects/ecommerce/plugins/Elastic/Model/Datasource/ElasticSource.php(1440): ElasticSource->_parseResponse(Object(HttpSocketResponse))
#2 /Projects/ecommerce/plugins/Elastic/Model/Datasource/ElasticSource.php(1402): ElasticSource->execute('get', 'currencies', '_count', Array)
#3 /Projects/ecommerce/plugins/Elastic/Model/Datasource/ElasticSource.php(256): ElasticSource->__call('get', Array)
#4 /Projects/ecommerce/plugins/Elastic/Model/Datasource/ElasticSource.php(256): ElasticSource->get('currencies', '_count', Array)
#5 /Projects/ecommerce/lib/Cake/Model/Model.php(3031): ElasticSource->read(Object(Currency), Array)
#6 /Projects/ecommerce/lib/Cake/Model/Model.php(3003): Model->_readDataSource('count', Array)
#7 /Projects/ecommerce/lib/Cake/Model/Model.php(2913): Model->find('count', Array)
#8 /Projects/ecommerce/lib/Cake/Model/Model.php(1818): Model->exists()
#9 /Projects/ecommerce/lib/Cake/Model/Model.php(1745): Model->_doSave(Array, Array)
#10 /Projects/ecommerce/lib/Cake/Model/Model.php(2506): Model->save(Array, Array)
#11 /Projects/ecommerce/lib/Cake/Model/Model.php(2341): Model->saveAssociated(Array, Array)
#12 /Projects/ecommerce/lib/Cake/Model/Model.php(2271): Model->saveMany(Array, Array)
#13 /Projects/ecommerce/plugins/Elastic/Console/Command/ElasticShell.php(310): Model->saveAll(Array, Array)
#14 /Projects/ecommerce/lib/Cake/Console/Shell.php(458): ElasticShell->index()
#15 /Projects/ecommerce/lib/Cake/Console/ShellDispatcher.php(212): Shell->runCommand('index', Array)
#16 /Projects/ecommerce/lib/Cake/Console/ShellDispatcher.php(66): ShellDispatcher->dispatch()
#17 /Projects/ecommerce/app/Console/cake.php(47): ShellDispatcher::run(Array)
#18 {main}

Error on Elasticsearch "6.2.2"

Hi, I got this error while running the mapping command.

Error: ElasticSearch Error: Content-Type header [application/x-www-form-urlencoded] is not supported Status: 406

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.