Giter VIP home page Giter VIP logo

ravendb-php-client's Introduction

PHP client for RavenDB NoSQL Database

Installation

You can install library to your project via Composer

$ composer require ravendb/ravendb-php-client

Releases

  • All client versions 5.2.x are fully compatible with and support RavenDB server releases 5.4 and 6.0.

  • Click here to view all Releases and Changelog.

Documentation

This readme provides short examples for the following:

Getting started,
Crud example,
Query documents,
Attachments,
Time series,
Revisions,
Suggestions,
Patching,
Using classes,
PHP usage,
Working with secure server,
Running tests

For more information go to the online RavenDB Documentation.

For more information on how to use RavenDB with Laravel check out the Raven Laravel Demo Application

Getting started

  1. Require the DocumentStore class from the ravendb package
use RavenDB\Documents\DocumentStore;
  1. Initialize the document store (you should have a single DocumentStore instance per application)
    $store = new DocumentStore('http://live-test.ravendb.net', 'databaseName');
    $store->initialize();
  1. Open a session
    $session = $store->openSession();
  1. Call saveChanges() when you're done
    $user = $session->load('users/1-A'); // Load document
    $user->setPassword(PBKDF2('new password')); // Update data
    
    $session->saveChanges(); // Save changes
    // Data is now persisted
    // You can proceed e.g. finish web request
    

CRUD example

Store documents

$product = new Product();
$product->setTitle("iPhone X");
$product->setPrice(999.99);
$product->setCurrency("USD");
$product->setStorage(64);
$product->setManufacturer("Apple");
$product->setInStock(true);

$session->store($product, 'products/1-A');
echo $product->id; // products/1-A

$session->saveChanges();

storing docs with same ID in same session should throw

Load documents

$product = $session->load(Product::class, 'products/1-A');
echo $product->getTitle(); // iPhone X
echo $product->getId();    // products/1-A

Load documents with include

// users/1
// {
//      "name": "John",
//      "kids": ["users/2", "users/3"]
// }

$session = $store->openSession();

try {
    $user1 = $session
        ->include("kids")
        ->load("users/1");
        // Document users/1 and all docs referenced in "kids"
        // will be fetched from the server in a single request.

    $user2 = $session->load("users/2"); // this won't call server again

    $this->assertNotNull($user1);
    $this->assertNotNull($user2);
    $this->assertEqual(1, $session->advanced()->getNumberOfRequests());
} finally {
    $session->close();
}

can load with includes

Update documents

$product = $session->load(Product::class, 'products/1-A');
$product->setInStock(false);
$product->setLastUpdate(new Date());
$session->saveChanges();
// ...
$product = $session->load(Product::class, 'products/1-A');
echo $product->getInStock();    // false
echo $product->getLastUpdate(); // the current date

Delete documents

  1. Using entity
$product = $session->load('products/1-A');
$session->delete($product);
$session->saveChanges();

$product = $session->load('products/1-A');
$this->assertNull($product); // null
  1. Using document ID
$session->delete('products/1-A');

delete doc by entity
delete doc by ID
onBeforeDelete is called before delete by ID
cannot delete untracked entity
loading deleted doc returns null

Query documents

  1. Use query() session method:

Query by collection:

$query = $session->query(Product::class, Query::collection('products'));

Query by index name:

$query = $session->query(Product::class, Query::indexName('productsByCategory'));

Query by index:

$query = $session->query(Product::class, Products_ByCategory::class);

Query by entity type:

$query = $session->query(Product::class);
  1. Build up the query - apply search conditions, set ordering, etc.
    Query supports chaining calls:
$query
    ->waitForNonStaleResults()
    ->usingDefaultOperator('AND') 
    ->whereEquals('manufacturer', 'Apple')
    ->whereEquals('in_stock', true)
    ->whereBetween('last_update', new DateTime('- 1 week'), new DateTime())
    ->orderBy('price');
  1. Execute the query to get results:
$results = $query->toList(); // get all results
// ...
$firstResult = $query->first(); // gets first result
// ...
$single = $query->single();  // gets single result 

Query methods overview

selectFields() - projections using a single field

// RQL
// from users select name

// Query
$userNames = $session->query(User::class)
    ->selectFields("name")
    ->toList();

// Sample results
// John, Stefanie, Thomas

query single property

can_project_id_field

selectFields() - projections using multiple fields

// RQL
// from users select name, age

// Query
$session->query(User::class)
    ->selectFields([ "name", "age" ])
    ->toList();

// Sample results
// [ [ name: 'John', age: 30 ],
//   [ name: 'Stefanie', age: 25 ],
//   [ name: 'Thomas', age: 25 ] ]

query with projection
can_project_id_field

distinct()

// RQL
// from users select distinct age

// Query
$session->query(User::class)
    ->selectFields("age")
    ->distinct()
    ->toList();

// Sample results
// [ 30, 25 ]

query distinct

whereEquals() / whereNotEquals()

// RQL
// from users where age = 30 

// Query
$session->query(User::class)
    ->whereEquals("age", 30)
    ->toList();

// Sample results
// [ User {
//    name: 'John',
//    age: 30,
//    kids: [...],
//    registeredAt: 2017-11-10T23:00:00.000Z } ]

where equals
where not equals

whereIn()

// RQL
// from users where name in ("John", "Thomas")

// Query
$session->query(User::class)
    ->whereIn("name", ["John", "Thomas"])
    ->toList();

// Sample results
// [ User {
//     name: 'John',
//     age: 30,
//     registeredAt: 2017-11-10T23:00:00.000Z,
//     kids: [...],
//     id: 'users/1-A' },
//   User {
//     name: 'Thomas',
//     age: 25,
//     registeredAt: 2016-04-24T22:00:00.000Z,
//     id: 'users/3-A' } ]

query with where in

whereStartsWith() / whereEndsWith()

// RQL
// from users where startsWith(name, 'J')

// Query
$session->query(User::class)
    ->whereStartsWith("name", "J")
    ->toList();

// Sample results
// [ User {
//    name: 'John',
//    age: 30,
//    kids: [...],
//    registeredAt: 2017-11-10T23:00:00.000Z } ]

query with where clause

whereBetween()

// RQL
// from users where registeredAt between '2016-01-01' and '2017-01-01'

// Query
$session->query({ collection: "users" })
    ->whereBetween("registeredAt", DateTime::createFromFormat('Y-m-d', '2016-01-01'), DateTime::createFromFormat('Y-m-d', '2017-01-01'))
    ->toList();

// Sample results
// [ User {
//     name: 'Thomas',
//     age: 25,
//     registeredAt: 2016-04-24T22:00:00.000Z,
//     id: 'users/3-A' } ]

query with where between

whereGreaterThan() / whereGreaterThanOrEqual() / whereLessThan() / whereLessThanOrEqual()

// RQL
// from users where age > 29

// Query
$session->query(User::class)
    ->whereGreaterThan("age", 29)
    ->toList();

// Sample results
// [ User {
//   name: 'John',
//   age: 30,
//   registeredAt: 2017-11-10T23:00:00.000Z,
//   kids: [...],
//   id: 'users/1-A' } ]

query with where less than
query with where less than or equal
query with where greater than
query with where greater than or equal

whereExists()

Checks if the field exists.

// RQL
// from users where exists("age")

// Query
$session->query(User::class)
    ->whereExists("kids")
    ->toList();

// Sample results
// [ User {
//   name: 'John',
//   age: 30,
//   registeredAt: 2017-11-10T23:00:00.000Z,
//   kids: [...],
//   id: 'users/1-A' } ]

query where exists

containsAny() / containsAll()

// RQL
// from users where kids in ('Mara')

// Query
$session->query(User::class)
    ->containsAll("kids", ["Mara", "Dmitri"])
    ->toList();

// Sample results
// [ User {
//   name: 'John',
//   age: 30,
//   registeredAt: 2017-11-10T23:00:00.000Z,
//   kids: ["Dmitri", "Mara"]
//   id: 'users/1-A' } ]

queries with contains

Perform full-text search.

// RQL
// from users where search(kids, 'Mara')

// Query
$session->query(User::class)
    ->search("kids", "Mara Dmitri")
    ->toList();

// Sample results
// [ User {
//   name: 'John',
//   age: 30,
//   registeredAt: 2017-11-10T23:00:00.000Z,
//   kids: ["Dmitri", "Mara"]
//   id: 'users/1-A' } ]

query search with or
query_CreateClausesForQueryDynamicallyWithOnBeforeQueryEvent

openSubclause() / closeSubclause()

// RQL
// from users where exists(kids) or (age = 25 and name != Thomas)

// Query
$session->query(User::class)
    ->whereExists("kids")
    ->orElse()
    ->openSubclause()
        ->whereEquals("age", 25)
        ->whereNotEquals("name", "Thomas")
    ->closeSubclause()
    ->toList();

// Sample results
// [ User {
//     name: 'John',
//     age: 30,
//     registeredAt: 2017-11-10T23:00:00.000Z,
//     kids: ["Dmitri", "Mara"]
//     id: 'users/1-A' },
//   User {
//     name: 'Stefanie',
//     age: 25,
//     registeredAt: 2015-07-29T22:00:00.000Z,
//     id: 'users/2-A' } ]

working with subclause

not()

// RQL
// from users where age != 25

// Query
$session->query(User::class)
    ->not()
    ->whereEquals("age", 25)
    ->toList();

// Sample results
// [ User {
//   name: 'John',
//   age: 30,
//   registeredAt: 2017-11-10T23:00:00.000Z,
//   kids: ["Dmitri", "Mara"]
//   id: 'users/1-A' } ]

query where not

orElse() / andAlso()

// RQL
// from users where exists(kids) or age < 30

// Query
$session->query(User::class)
    ->whereExists("kids")
    ->orElse()
    ->whereLessThan("age", 30)
    ->toList();

// Sample results
//  [ User {
//     name: 'John',
//     age: 30,
//     registeredAt: 2017-11-10T23:00:00.000Z,
//     kids: [ 'Dmitri', 'Mara' ],
//     id: 'users/1-A' },
//   User {
//     name: 'Thomas',
//     age: 25,
//     registeredAt: 2016-04-24T22:00:00.000Z,
//     id: 'users/3-A' },
//   User {
//     name: 'Stefanie',
//     age: 25,
//     registeredAt: 2015-07-29T22:00:00.000Z,
//     id: 'users/2-A' } ]

working with subclause

usingDefaultOperator()

If neither andAlso() nor orElse() is called then the default operator between the query filtering conditions will be AND .
You can override that with usingDefaultOperator which must be called before any other where conditions.

// RQL
// from users where exists(kids) or age < 29

// Query
$session->query(User::class)
    ->usingDefaultOperator("OR") // override the default 'AND' operator
    ->whereExists("kids")
    ->whereLessThan("age", 29)
    ->toList();

// Sample results
//  [ User {
//     name: 'John',
//     age: 30,
//     registeredAt: 2017-11-10T23:00:00.000Z,
//     kids: [ 'Dmitri', 'Mara' ],
//     id: 'users/1-A' },
//   User {
//     name: 'Thomas',
//     age: 25,
//     registeredAt: 2016-04-24T22:00:00.000Z,
//     id: 'users/3-A' },
//   User {
//     name: 'Stefanie',
//     age: 25,
//     registeredAt: 2015-07-29T22:00:00.000Z,
//     id: 'users/2-A' } ]

orderBy() / orderByDesc() / orderByScore() / randomOrdering()

// RQL
// from users order by age

// Query
$session->query(User::class)
    ->orderBy("age")
    ->toList();

// Sample results
// [ User {
//     name: 'Stefanie',
//     age: 25,
//     registeredAt: 2015-07-29T22:00:00.000Z,
//     id: 'users/2-A' },
//   User {
//     name: 'Thomas',
//     age: 25,
//     registeredAt: 2016-04-24T22:00:00.000Z,
//     id: 'users/3-A' },
//   User {
//     name: 'John',
//     age: 30,
//     registeredAt: 2017-11-10T23:00:00.000Z,
//     kids: [ 'Dmitri', 'Mara' ],
//     id: 'users/1-A' } ]

query random order
order by AlphaNumeric
query with boost - order by score

take()

Limit the number of query results.

// RQL
// from users order by age

// Query
$session->query(User::class)
    ->orderBy("age") 
    ->take(2) // only the first 2 entries will be returned
    ->toList();

// Sample results
// [ User {
//     name: 'Stefanie',
//     age: 25,
//     registeredAt: 2015-07-29T22:00:00.000Z,
//     id: 'users/2-A' },
//   User {
//     name: 'Thomas',
//     age: 25,
//     registeredAt: 2016-04-24T22:00:00.000Z,
//     id: 'users/3-A' } ]

query skip take

skip()

Skip a specified number of results from the start.

// RQL
// from users order by age

// Query
$session->query(User::class)
    ->orderBy("age") 
    ->take(1) // return only 1 result
    ->skip(1) // skip the first result, return the second result
    ->toList();

// Sample results
// [ User {
//     name: 'Thomas',
//     age: 25,
//     registeredAt: 2016-04-24T22:00:00.000Z,
//     id: 'users/3-A' } ]

query skip take

Getting query statistics

Use the statistics() method to obtain query statistics.

// Query
$stats = new QueryStatistics();
$results = $session->query(User::class)
    ->whereGreaterThan("age", 29)
    ->statistics($stats)
    ->toList();

// Sample results
// QueryStatistics {
//   isStale: false,
//   durationInMs: 744,
//   totalResults: 1,
//   skippedResults: 0,
//   timestamp: 2018-09-24T05:34:15.260Z,
//   indexName: 'Auto/users/Byage',
//   indexTimestamp: 2018-09-24T05:34:15.260Z,
//   lastQueryTime: 2018-09-24T05:34:15.260Z,
//   resultEtag: 8426908718162809000 }

all() / first() / single() / count()

all() - returns all results

first() - first result only

single() - first result, throws error if there's more entries

count() - returns the number of entries in the results (not affected by take())

query first and single
query count

Attachments

Store attachments

$doc = new User();
$doc->setName('John');

// Store a document, the entity will be tracked.
$session->store($doc);

// Get read stream or buffer to store
$fileStream = file_get_contents("../photo.png");

// Store attachment using entity
$session->advanced()->attachments()->store($doc, "photo.png", $fileStream, "image/png");

// OR store attachment using document ID
$session->advanced()->attachments()->store($doc->getId(), "photo.png", $fileStream, "image/png");

// Persist all changes
$session->saveChanges();

can put attachments

Get attachments

// Get an attachment
$attachment = $session->advanced()->attachments()->get($documentId, "photo.png")

// Attachment.details contains information about the attachment:
//     { 
//       name: 'photo.png',
//       documentId: 'users/1-A',
//       contentType: 'image/png',
//       hash: 'MvUEcrFHSVDts5ZQv2bQ3r9RwtynqnyJzIbNYzu1ZXk=',
//       changeVector: '"A:3-K5TR36dafUC98AItzIa6ow"',
//       size: 4579 
//     }

// Attachment.data is a Readable.
$fileBytes = $attachment->getData();
file_put_contents('../photo.png', $fileBytes);

can get & delete attachments

Check if attachment exists

$session->advanced()->attachments()->exists($doc->getId(), "photo.png");
// true

$session->advanced()->attachments()->exists($doc->getId(), "not_there.avi");
// false

attachment exists 2

Get attachment names

// Use a loaded entity to determine attachments' names
$session->advanced()->attachments()->getNames($doc);

// Sample results:
// [ { name: 'photo.png',
//     hash: 'MvUEcrFHSVDts5ZQv2bQ3r9RwtynqnyJzIbNYzu1ZXk=',
//     contentType: 'image/png',
//     size: 4579 } ]

get attachment names 2

TimeSeries

Store time series

$session = $store->openSession();

// Create a document with time series
$session->store(new User(), "users/1");
$tsf = $session->timeSeriesFor("users/1", "heartbeat");

// Append a new time series entry
$tsf->append(new DateTime(), 120);

$session->saveChanges();

canCreateSimpleTimeSeries
usingDifferentTags
canStoreAndReadMultipleTimestamps
canStoreLargeNumberOfValues
shouldDeleteTimeSeriesUponDocumentDeletion

Get time series for document

$session = $store->openSession();

// Get time series for document by time series name
$tsf = $session->timeSeriesFor("users/1", "heartbeat");

// Get all time series entries
$heartbeats = $tsf->get();

canCreateSimpleTimeSeries
canStoreLargeNumberOfValues
canRequestNonExistingTimeSeriesRange
canGetTimeSeriesNames2
canSkipAndTakeTimeSeries

Revisions

NOTE: Please make sure revisions are enabled before trying the below.

$user = new User();
$user->setName("Marcin");
$user->setAge(30);
$user->setPet("Cat");

$session = $store->openSession();

// Store a document
$session->store($user, "users/1");
$session->saveChanges();

// Modify the document to create a new revision
$user->setName("Roman");
$user->setAge(40);
$session->saveChanges();

// Get revisions
$revisions = $session->advanced()->revisions()->getFor("users/1");

// Sample results:
// [ { name: 'Roman',
//     age: 40,
//     pet: 'Cat',
//     '@metadata': [Object],
//     id: 'users/1' },
//   { name: 'Marcin',
//     age: 30,
//     pet: 'Cat',
//     '@metadata': [Object],
//     id: 'users/1' }
// ]

Suggestions

Suggest options for similar/misspelled terms

// Some documents in users collection with misspelled name term
// [ User {
//     name: 'Johne',
//     age: 30,
//     ...
//     id: 'users/1-A' },
//   User {
//     name: 'Johm',
//     age: 31,
//     ...
//     id: 'users/2-A' },
//   User {
//     name: 'Jon',
//     age: 32,
//     ...
//     id: 'users/3-A' },
// ]

// Static index definition
class UsersIndex extends AbstractJavaScriptIndexCreationTask {
    public function __construct() {
        parent::__construct();
                
        $this->map = "from user in docs.users select new { user.name }";
        
        // Enable the suggestion feature on index-field 'name'
        $this->suggestion("name"); 
    }
}

// ...
$session = $store->openSession();

// Query for similar terms to 'John'
// Note: the term 'John' itself will Not be part of the results

$suggestedNameTerms = $session->query(User::class, UsersIndex::class)
    ->suggestUsing(function($x) { return $x->byField("name", "John"); }) 
    ->execute();

// Sample results:
// { name: { name: 'name', suggestions: [ 'johne', 'johm', 'jon' ] } }

Advanced patching

// Increment 'age' field by 1
$session->advanced()->increment("users/1", "age", 1);

// Set 'underAge' field to false
$session->advanced->patch("users/1", "underAge", false);

$session->saveChanges();

can patch
can patch complex
can add to array
can increment
patchWillUpdateTrackedDocumentAfterSaveChanges
can patch single document

Using classes for entities

  1. Define your model as class. Attributes should be just public properties:
class Product {

    public ?string $id = null,
    public string $title = '',
    public int $price = 0,
    public string $currency = 'USD',
    public int $storage = 0,
    public string $manufacturer = '',
    public bool $in_stock = false,
    public ?DateTime $last_update = null

    public function __construct(
        $id = null,
        $title = '',
        $price = 0,
        $currency = 'USD',
        $storage = 0,
        $manufacturer = '',
        $in_stock = false,
        $last_update = null
    ) {
        $this->id = $id;
        $this->title = $title;
        $this->price = $price;
        $this->currency = $currency;
        $this->storage = $storage;
        $this->manufacturer = $manufacturer;
        $this->in_stock = $in_stock;
        $this->last_update = $last_update ?? new DateTime();
    }
}
  1. To store a document pass its instance to store().
    The collection name will automatically be detected from the entity's class name.
use models\Product;

$product = new Product(
  null, 'iPhone X', 999.99, 'USD', 64, 'Apple', true, new Date('2017-10-01T00:00:00'));

$product = $session->store($product);

var_dump($product instanceof Product);                // true
var_dump(str_starts_with($product->id, 'products/')); // true

$session->saveChanges();
  1. Loading a document
$product = $session->load('products/1-A');
var_dump($product instanceof Product); // true
var_dump($product->id);                // products/1-A
  1. Querying for documents
$products = $session->query(Product::class)->toList();

foreach($products as $product) {
  var_dump($product instanceof Product);                // true
  var_dump(str_starts_with($product->id, 'products/')); // true
});

Usage with PHP

PHP typings are embedded into the package. Make sure to close session when you finish your work with it.

// file models/product.php
class Product {
    public ?string $id = null,
    public string $title = '',
    public int $price = 0,
    public string $currency = 'USD',
    public int $storage = 0,
    public string $manufacturer = '',
    public bool $in_stock = false,
    public ?DateTime $last_update = null
    
    public function __construct(
        $id = null,
        $title = '',
        $price = 0,
        $currency = 'USD',
        $storage = 0,
        $manufacturer = '',
        $in_stock = false,
        $last_update = null
    ) {
        $this->id = $id;
        $this->title = $title;
        $this->price = $price;
        $this->currency = $currency;
        $this->storage = $storage;
        $this->manufacturer = $manufacturer;
        $this->in_stock = $in_stock;
        $this->last_update = $last_update ?? new DateTime();
    }
}

// file app.php
use models\Product;
use RavenDB\Documents\DocumentStore;
use RavenDB\Documents\Session\DocumentSession;

$store = new DocumentStore('url', 'database name');
try {
    $store->initialize();
    
    $productId = null;
    
    /** @var DocumentSession $session */
    $session = $store->openSession();
    try {
        $product = new Product(
          null, 'iPhone X', 999.99, 'USD', 64, 'Apple', true, new Date('2017-10-01T00:00:00'));

        $session->store($product);
        $session->saveChanges();
        
        var_dump($product instanceof Product);                // true
        var_dump(str_starts_with($product->id, 'products/')); // true
        
        $productId = $product->id;
    } finally {
        $session->close();    
    }
    
    $session = $store->openSession();
    try {
        /** @var Product $product */
        $product = $session->load(Product::class, $productId);
        
        var_dump($product instanceof Product);                // true
        var_dump($product->id); // products/1-A
        
        /** @var array<Product> $products */
        $products = $session->query(Query::collection('Products'))
                    ->waitForNonStaleResults()
                    ->whereEquals('manufacturer', 'Apple')
                    ->whereEquals('in_stock', true)
                    ->whereBetween('last_update', new DateTime('- 1 week'), new DateTime())
                    ->whereGreaterThanOrEqual('storage', 64)
                    ->toList();
    
        foreach ($products as $product) {
            var_dump($product instanceof Product);                // true
            var_dump(str_starts_with($product->id, 'products/')); // true
        }
       
    } finally {
        $session->close();    
    }
    
} finally {
    $store->close();
}

Working with a secure server

Your certificate and server certificate should be saved in PEM format to your machine.

  1. Create AuthOptions:
$authOptions = AuthOptions::pem(
    '../clientCertPath.pem',
    'clientCertPass',
    '../serverCaCertPath.pem'
);
  1. Pass auth options to DocumentStore object:
$store = new DocumentStore('url', 'databaseName');
$store->setAuthOptions($authOptions); // use auth options to connect on database
$store->initialize();

Running tests

Clone the repository:

git clone https://github.com/ravendb/ravendb-php-client

Install dependencies:

composer install

Run RavenDB server

https://a.phptest.development.run

Set environment variables.

# Set the following environment variables:
#
# - Certificate hostname
# RAVENDB_PHP_TEST_HTTPS_SERVER_URL=https://a.phptest.development.run
#
# RAVENDB_PHP_TEST_CA_PATH=
#
# - Certificate path for tests requiring a secure server:
# RAVENDB_PHP_TEST_CERTIFICATE_PATH=
#
# - Certificate for client
# RAVENDB_TEST_CLIENT_CERT_PATH=
# RAVENDB_TEST_CLIENT_CERT_PASSPHRASE=
#
# - For some tests, Developers licence is required in order to run them all 
# RAVEN_LICENSE=

Run PHPUnit

./vendor/bin/phpunit

Bug Tracker

http://issues.hibernatingrhinos.com/issues/RDBC


License

The MIT License (MIT). Please see License File for more information.

ravendb-php-client's People

Contributors

alxsabo avatar dejanmilicic avatar ml054 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

ravendb-php-client's Issues

Query doesn't return results if count() is called before

After updating package version to latest, this issue started to occur.

If $query->count() is called, then toList() returns empty array

try {
    $query = $session->query(Site::class)->whereEquals("userId", $userId);

    if ($query->count() <= 0) {
        return [];
    }

    return $query->toList(); // returns []
} finally {
    $session->close();
}

But if the count call is taken away, then it works.

try {
    $query = $session->query(Site::class)->whereEquals("userId", $userId);

    return $query->toList(); // returns [{}, {}, {}, ...]
} finally {
    $session->close();
}

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.