Giter Site home page Giter Site logo

graphql-search-api's People

Contributors

carolpettirossi avatar digiterra-korepov avatar duartegarin avatar el7cosmos avatar joaogarin avatar mdance avatar murznn avatar rwohleb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

graphql-search-api's Issues

Expose search_api_relevance score to query

How do I access the search_api_relevance score in a query? This is a field that I can expose, filter and sort by in a Drupal view via search_api but how can I get this same data in a GraphQL query?

If it's not currently available, it would be very helpful to be able to use this field for debugging and custom sort purposes.

Thanks!

Update documentation with facet tags

We can use tags to exclude certain conditions from being counted by facets.

The idea was to introduce the concept of facet exclusion. That is, to exclude facets from being altered by conditions of that same facet in the main query.

For example:

  searchAPISearch(index_id: "anabranch_connect_index", condition_group:{
    conjunction:AND,
    groups: [
        {
          conjunction: OR,
          tags:["facet:study_fields"],
          conditions: [
            {operator: "=", name: "study_fields", value: "Finance"},
          ]
        },
        {
          conjunction: OR,
          tags:["facet:article_type"],
          conditions: [
            {operator: "=", name: "article_type", value: "Advice"},
          ]
        },
      ]
    
  },

Need to update the docs with this.

How to filter for an active facet?

I could manage to get search_api_graphql working as expected. Now i'm stuck with a question regarding facets parameter input.

I can query for facets and get the list of available facets back. That's fine.

{
  searchAPISearch(
    index_id: "search_index", 
    language: "de", 
    range: {start: 1, end: 5},
    facets: [
        {operator: "=", field: "field_a", limit: 1, min_count: 0, missing: false}
    	{operator: "=", field: "field_b", limit: 1, min_count: 0, missing: false}
    ]
  ) 
  {
    facets {
      name
      values {
        filter
        count
      }
    }
    documents {
      ... on SearchIndexDoc {
        title
      }
    }
  }
}

I get the correct result (that's also fine).

{
  "data": {
    "searchAPISearch": {
      "result_count": 3,
      "facets": [
        {
          "name": "field_a",
          "values": [
            {
              "filter": "34",
              "count": 3
            }
          ]
        },
        {
          "name": "field_a",
          "values": [
            {
              "filter": "35",
              "count": 1
            }
          ]
        },
        {
          "name": "field_b",
          "values": [
            {
              "filter": "42",
              "count": 2
            }
          ]
        },
        {
          "name": "field_b",
          "values": [
            {
              "filter": "10",
              "count": 1
            }
          ]
        }
      ],
      "documents": [
        {
          "title": "Hello World 1"
        },
        {
          "title": "Hello World 2"
        }
      ]
    }
  }
}

So here is my question: How can i pass a filtered facet item to the query? So lets say i want to filter for field_a=34 and field_b=10 and get all results that matches these elements. Is that done with conditions and condition groups (https://graphql-search-api.readthedocs.io/en/latest/search-parameters/#conditions)?

Mulitvalued custom field results are incomplete

Original request was opened here: https://www.drupal.org/project/graphql_search_api/issues/3183710 @Kyubbi instructed to open here on github instead of Drupal.org

Hi! I would appreciate some guidance from someone that has already been there and has the battle scars to share. AdvThanksance!!!

The module stack is pretty tall and it has been difficult to narrow down any further. Looking into the Common pitfalls and FAQs has not led to any inspiration. I thought to post now but I will look at upgrading to the latest recommended versions of the module. Previous recommendations to upgrade to search_aol_solr to 4.1 from 8.x-1.5 did not resulted in any improvement.

Givens:

  • Created a custom field (group__members) to include data for indexing and faceting in Solr.
  • The required data resides outside of the basic content type.
  • The source of data for the custom field is a multi-valued set of names.
  • The search results should also return a multi-valued set of names.

Knowns:

  • Able to index into Solr. See Evidence 2.
  • As a positive, able to get results with facets using graphql_search_api. See Evidence 3.
  • The custom field is not unpacked correctly. In GraphQL Explorer, it is returning just the first name as a single string instead of a multi-valued set.

Relevant versions and enabled modules:

  • Apache Solr 4.5.1
  • Drupal core 8.8.8 (Upgrade to 8.9 scheduled)
  • search_api 8.x-1.18 (Up-to-date)
  • search_api_solr 4.1.10 (Up-to-date)
  • search_api_solr_legacy 4.1.10 (Up-to-date)
  • graphql 8.x-3.1 (Up-to-date)
  • graphql_search_api 8.x-1.2 (Up-to-date)

Evidence 1: Custom field definition

namespace Drupal\brqc_custom\Plugin\search_api\processor;

use Drupal\search_api\Datasource\DatasourceInterface;
use Drupal\search_api\Item\ItemInterface;
use Drupal\search_api\Processor\ProcessorPluginBase;
use Drupal\search_api\Processor\ProcessorProperty;

/**
 * Adds a custom field to the indexed data.
 *
 * @SearchApiProcessor(
 *   id = "group__members",
 *   label = @Translation("Group Members"),
 *   description = @Translation("Add a custom field of group members to search index."),
 *   stages = {
 *     "add_properties" = 0,
 *   },
 *   locked = true,
 *   hidden = false,
 * )
 */
class GroupMembers extends ProcessorPluginBase {

  /**
   * machine name of the processor.
   * @var string
   */
  protected $processor_id = 'group__members';

  /**
   * {@inheritdoc}
   */
  public function getPropertyDefinitions(DatasourceInterface $datasource = NULL) {
    $properties = array();

    if (!$datasource) {
      $definition = array(
        'label' => $this->t('Group Members'),
        'description' => $this->t('Custom field for storing all members that belong to this group.'),
        'type' => 'string',
        'processor_id' => $this->getPluginId(),
      );
      $properties[$this->processor_id] = new ProcessorProperty($definition);
    }

    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public function addFieldValues(ItemInterface $item) {
    $entity = $item->getOriginalObject()->getValue();
    $entity_type = $entity->getEntityTypeId();

    // Use $entity to get custom field.
    // Determine the correct content type.
    if ($entity_type == 'group') {
      // Get members from the group. Temporarily using superhero secret identities.
      $members = [
        'John Doe',
        'Peter Parker',
        'Bruce Wayne',
        'Clark Kent',
        'Diana Prince',
        'Arthur Curry',
        'Jack Napier',
        'James Howlett',
      ];
      $temp_members = [];
      $random = rand(0, count($members));
      $member_keys = array_rand($members, $random);
      if (!is_array($member_keys)) {
        $temp_members[] = $members[$member_keys];
      }
      else {
        foreach ($member_keys as $member_key) {
          $temp_members[] = $members[$member_key];
        }
      }
      $members = $temp_members;

      // Add the group members in individually.
      if (count($members) > 0) {
        $fields = $this->getFieldsHelper()
          ->filterForPropertyPath($item->getFields(), NULL, $this->processor_id);
        foreach ($fields as $field) {
          foreach ($members as $member) {
            $field->addValue($member);
          }
        }
      }
    }
  }

}

Evidence 2: Selected portions of query results from Solr admin.

{
  "response": {
    "numFound": 1,
    "start": 0,
    "docs": [
      {
        … stuff deleted
        "sm_group__members": [
          "John Doe",
          "Peter Parker",
          "Bruce Wayne",
          "Clark Kent",
          "Diana Prince",
          "Arthur Curry",
          "Jack Napier",
          "James Howlett"
        ],
       … stuff deleted
      }
    ]
  }
}

Evidence 3: GraphQL Explorer results

  • Actual Results: group__members is just a single string with just one of the names.
  • Expected Results: group_members is an array of name strings.
{
  "data": {
    "searchAPISearch": {
      "count": 1,
      "facets": [
        {
          "name": "group__type",
          "values": [
            {
              "count": 1,
              "filter": "!"
            }
          ]
        },
        {
          "name": "group__members",
          "values": [
            {
              "count": 1,
              "filter": "Arthur Curry"
            },
            {
              "count": 1,
              "filter": "Bruce Wayne"
            },
            {
              "count": 1,
              "filter": "Clark Kent"
            },
            {
              "count": 1,
              "filter": "Diana Prince"
            },
            {
              "count": 1,
              "filter": "Jack Napier"
            },
            {
              "count": 1,
              "filter": "James Howlett"
            },
            {
              "count": 1,
              "filter": "John Doe"
            },
            {
              "count": 1,
              "filter": "Peter Parker"
            }
          ]
        }
      ],
      "documents": [
        {
          … stuff deleted
          "group__members": "John Doe",
          … stuff deleted
        }
      ]
    }
  }
}

How to get facets through GraphQL Search API ?

How does facets work? I'm using Drupal 9 and elasticsearch with elasticsearch-connector module and everything works fine except facets. They are always empty in the results. Should I install a facet module for this? Should I create a datasource for facets? How can I get facets to work with Drupal 9 and elasticsearch-connector module ? Thanks.

Using aggregated fields?

I may have found a bug with using aggregated fields.

I've currently have a sitewide search I'd like to search across all selected entities (ex: Nodes and Users) So I've set up a Solr index set up with aggregated fields called "title" which includes Node "Title" field as well as a User "name" field.

First when checking in the GraphQL explorer, the aggregated fields do not appear as available fields. So I then switched my query to do fulltext search on all the full text fields available, however, this results in an internal error from PHP

Notice: Undefined variable: results in Drupal\graphql_search_api\Plugin\GraphQL\Fields\SearchAPISearch->resolveValues() (line 62 of modules/contrib/graphql_search_api/src/Plugin/GraphQL/Fields/SearchAPISearch.php).
Drupal\graphql_search_api\Plugin\GraphQL\Fields\SearchAPISearch->resolveValues(NULL, Array, Object, Object)
iterator_to_array(Object) (Line: 163)

As well as a graphql error in the drupal logs:

Call to a member function getResultItems() on null

Drupal 9 support

In order this module work with Drupal 9, it should reply on GraphQL module version 4, but not 3.

Remove search api solr dependency

@joaogarin I just realised in the dependencies you added is search_api_solr as well.
That should be removed as this module doesn't depend on solr and can be used with any search backend.

Is it compatible with graphql 4.x version ?

Hi,
it seems to be a great module but it does not seem to work with the database backend.
Is it exclusively for Solr or Elastic search ?

I installed drupal graphql 4.x,
graphql search api 1.1,
search api and database search on drupal 8.8.5. I created a database search server, an index on a content type and a graphql server but I don't have the searchapisearch autocomplete in the graphiql explorer.

Anyway, thank you for this beautiful module!

screenshot

Graphql v4

There are plans to support the V4 of graphql?

Incorrect handling of emty-like field values

We are using this cool module to expose one of our Search API indexes to our front end.

It works really cool and has saved us days of hard work :) Just now that we are wrapping up the integration with Search API, I discovered a minor bug.

graphql_search_api would show NULL in the result for any value that looks like empty, i.e. empty($value) === TRUE (in PHP code).

The way I discovered it was the following, we had an integer field in the index and for many documents it would be 0. For such documents the graphql query would return null instead of the number.

Consider the following example. First, let me demonstrate my solr index - you will see it has the field its_kaltura_views_30d defined for multiple documents it is 0:

$ curl 'http://solr:8983/solr/drupal/select?q=index_id:kaltura&indent=on&fl=its_kaltura_mid,its_kaltura_views_30d'
{
  "response":{"numFound":6,"start":0,"docs":[
      {
        "its_kaltura_views_30d":15,
        "its_kaltura_mid":2},
      {
        "its_kaltura_views_30d":2,
        "its_kaltura_mid":3},
      {
        "its_kaltura_views_30d":0,
        "its_kaltura_mid":4},
      {
        "its_kaltura_views_30d":0,
        "its_kaltura_mid":5},
      {
        "its_kaltura_views_30d":0,
        "its_kaltura_mid":6},
      {
        "its_kaltura_views_30d":0,
        "its_kaltura_mid":7}]
  }}

Now if I request the same data over graphql endpoint, for non-zero I will see the correct number whereas all the zeros are replaced with NULL:
Screenshot from 2019-07-13 12-55-24

Sort the result set by relevance

Hello,

We want to sort the results by relevance but not able to do so.
We tried with different field names like "relevance", "score", "_score", "search_api_relevance" but every time getting error response.
Can any one let me know what should be the field name here to use?

Thanks in advance.

Using keys return "Internal server error"

In any case if I try to use keys, as in example below, in return I get "Internal server error".

query {
    searchAPISearch (
        index_id:"content",
        fulltext: {
            keys: "who",
            fields: ["title"]
        }
    ) {
        documents {
            ... on ContentDoc {
                title
            }
        }
    }
}

Empty string for keys doesn't throw error but also returns everything. After investigation, conditions also throws the same error

Not equal to (<>) operator on condition does not allow for NULL value

I'm trying to query for any items in an index that do not have a null string field. I have found that because the value field is required for the ConditionInput I cannot accomplish this.
I have tested making the value field in the ConditionInput optional and that does allow me to query for items where a given field is null.

Below is a working example after I modified the Drupal\graphql_search_api\Plugin\GraphQL\InputTypes\ConditionInput.php file

query{
  searchAPISearch(
    index_id:"default_solr_index",
    conditions: [{
      operator: "<>"
      name: "field_my_field"
    }]) {
    result_count
    documents {
      ... on DefaultSolrIndexDoc {
        nid
        title
        field_my_field
      }
    }
  }
}

I edited the ConditionInput.php file to look like

<?php

namespace Drupal\graphql_search_api\Plugin\GraphQL\InputTypes;

use Drupal\graphql\Plugin\GraphQL\InputTypes\InputTypePluginBase;

/**
 * Condition input type.
 *
 * @GraphQLInputType(
 *   id = "condition_input",
 *   name = "ConditionInput",
 *   fields = {
 *     "name" = "String!",
 *     "value" = "String",
 *     "operator" = "String"
 *   }
 * )
 */
class ConditionInput extends InputTypePluginBase {

}

Is there a different way I can query for an item without a null field value, or is this a bug?

Conjunction always using AND inside Condition Groups

Hello,

Looks like conjunction inside just below Condition Groups is always taking AND, irrespective of conjunction supplied.

Main Query

query ($key: [String]!, $offset: Int!, $limit: Int!, $filters: ConditionGroupInput, $sort: [SortInput]) {
      searchAPISearch(index_id: "myindex", condition_group: $filters, range: {offset: $offset, limit: $limit}, fulltext: {keys: $key}, sort: $sort) {
        count: result_count
        documents {
          ... on MyindexDoc  {
            group__title
            content__content_title
          }
        }
      }
    }

Scenario 1 : Supplied query variables (giving expected result)

{
  "offset": "0",
  "limit": 50,
  "key": "test",
  "filters": {
    "conjunction": "OR",
    "groups": [
      { 
        "conditions": [
          {
            "operator": "=",
            "name": "content__content_type",
            "value": "article"
          },
          {
            "operator": "=",
            "name": "content__field_is_application",
            "value": "yes"
          },
          {
            "operator": "<>",
            "name": "content__field_life_cycle__entity__name",
            "value": "Retired"
          }
        ]
      }
    ]
  },
  "sort": [
    {
      "field": "search_api_relevance",
      "value": "desc"
    }
  ]
}

Scenario 2 : Supplied query variables (giving expected result)

{
  "offset": "0",
  "limit": 50,
  "key": "test",
  "filters": {
    "conjunction": "OR",
    "groups": [
      {
        "conditions": [
          {
            "operator": ">",
            "name": "group__id",
            "value": "0"
          },
          {
            "operator": "<>",
            "name": "group__field_life_cycle__entity__title",
            "value": "Retired"
          },
          {
            "operator": "<>",
            "name": "group__field_is_live",
            "value": "0"
          }
        ]
      }
    ]
  },
  "sort": [
    {
      "field": "search_api_relevance",
      "value": "desc"
    }
  ]
}

Scenario 3 : Supplied query variables (giving 0 result, but it should be result of Scenario 1 + Scenario 2)

{
  "offset": "0",
  "limit": 50,
  "key": "test",
  "filters": {
    "conjunction": "OR",
    "groups": [
      { 
        "conditions": [
          {
            "operator": "=",
            "name": "content__content_type",
            "value": "article"
          },
          {
            "operator": "=",
            "name": "content__field_is_application",
            "value": "yes"
          },
          {
            "operator": "<>",
            "name": "content__field_life_cycle__entity__name",
            "value": "Retired"
          }
        ]
      },
      {
        "conditions": [
          {
            "operator": ">",
            "name": "group__id",
            "value": "0"
          },
          {
            "operator": "<>",
            "name": "group__field_life_cycle__entity__title",
            "value": "Retired"
          },
          {
            "operator": "<>",
            "name": "group__field_is_live",
            "value": "0"
          }
        ]
      }
    ]
  },
  "sort": [
    {
      "field": "search_api_relevance",
      "value": "desc"
    }
  ]
}

What I assume conjunction just above groups always using AND operation between groups objects, but I want to use OR here.
Am I doing something wrong?

resolveValues will error out if query has a fatal error

If $results errors or is NULL:

  public function resolveValues($value, array $args, ResolveContext $context, ResolveInfo $info) {

    // Load up the index passed in argument.
    $this->index = $this->entityTypeManager->getStorage('search_api_index')->load($args['index_id']);

    // Prepare the query with our arguments.
    $this->prepareSearchQuery($args);

    // Execute search.
    try {
      $results = $this->query->execute();
    }
    // Handle error, check exception type -> SearchApiException ?
    catch (\Exception $exception) {
      $this->logger->get('graphql_search_api')->error($exception->getMessage());
    }

    // Get search response from results.
    $search_response = $this->getSearchResponse($results);

    // Add the result count to the response.
    $search_response['result_count'] = $results->getResultCount();

    // Set response type.
    $search_response['type'] = 'SearchAPIResult';

    yield $search_response;

  }

I get a PHP notice:

"Notice: Undefined variable: results in Drupal\graphql_search_api\Plugin\GraphQL\Fields\SearchAPISearch->resolveValues() (line 97 of modules/contrib/graphql_search_api/src/Plugin/GraphQL/Fields/SearchAPISearch.php)."

In the browser I get a JS 500 error about an unexpected token.

This turns out to be (a rather common case) if you are running Solr and the Solr results are stale and return items that aren't in Drupal (like say, syncing a prod database to dev) and Drupal cannot load the item to display in a result. This is a little hard to track down if you aren't familiar with Search API and Solr.

I think the 3 lines pertaining to $search_response should be moved into the try block, and the catch block should have:

    $search_response = [];
    $search_response['result_count'] = 0;
    $search_response['type'] = 'SearchAPIResult';

This way at least the client doesn't break down and "empty results" message configured with Search will work.

Conflicts with multiple indexes

I am facing an issue with two indexes (on two solr cores). When having the same fields e.g. nid with an identical machine name in both indexes, this field can only be retrieved for one index.

Lets assume i have 2 indexes: "Index A" and "Index B" and add this field:
image

My query looks like this and works, as expected:

{
  searchAPISearch(
    index_id: "index_a",
    language: "de",
    range: {start: 1, end: 50}
  ) {
    result_count
    documents {
      ... on IndexADoc {
        nid
      }
    }
}
}

For the query to Index B i get "Cannot query field \"nid\" on type \"IndexBDoc\". The schema inspector doesn't show them (only fields that are not in Index A are shown).

{
  searchAPISearch(
    index_id: "index_b",
    language: "de",
    range: {start: 1, end: 50}
  ) {
    result_count
    documents {
      ... on IndexBDoc {
        nid
      }
    }
}
}

Renaming the machine name to nid_1 and modifying the query accordingly works as a workaround.

(Please not, that the queries above are "constructed" manually and not copied 1:1 from my client project. So they might contain an error.)

Incorrect field names in range

@joaogarin When we added range delimiters, we assumed that start equals offset (correct) and end equals the end of the range, when in fact it is a "limit".

This means that if I want results on page 2 (on a 5 records per page) I should specify "start" as 5 and "end" as 5.

I propose we refactor this to "offset" and "limit" to be consistent with the rest of GraphQL module implementations.

However, this is a breaking change given that all consumers need to update their implementations.

Facets with Language

I am using the language specification in my query just fine and the results are as expected. However, the facets still return items in all languages. Is there a way to adhere the language specification to the facets?

My query

query {
  searchAPISearch(
    index_id: "content"
    range: {offset: 0, limit: 25}
    language: "en"
    facets: [
      {operator: "=", field: "product", limit: 0, min_count: 0, missing: false}
    ]
  ) {
    result_count
    documents {
      ... on ContentDoc {
        nid
        url
        title
        product
        type
        version
        langcode
      }
    }
    facets {
      name
      values {
        count
        name: filter
      }
    }
  }
}

Add configurable cache max age to GraphQL Search API query results

Caching is vital -- but when working with a very large or otherwise busy Search API index, the Search API tracking can report a successfully indexed document to GraphQL Search API while it's not actually been indexed in solr (or whatever backend) yet before the client has made the query following having entered new records.

In our application, we found it helpful to artificially reduce cache max-age to allow more opportunity for the indexer to present the freshest data as soon as it is available.

I am working on a patch for this now.

D9 readiness

Upgrade status reports only one warning:

Add core_version_requirement: ^8 || ^9 to designate that the module is compatible with Drupal 9. See https://drupal.org/node/3070687.

Please, review #36 to get the module available on D9 too.

New release

Hey @duartegarin do you see a problem in making a new release with b7a8811 ? Maybe an Rc2.

Got it ready to go, just want to make sure you'r ok with it.

Search Highlight Processor

I have the search highlight processor enabled but I don't see a way to access it through this API and I saw no mention of it anywhere. Is this something I have to do manually by going through each field which seems very tedious or is there a way to access this?

Condition Groups not taking effect

Hopefully this is not user error; however, it seems that condition groups are not properly applying for any fields. I have the following query that is supposed to only results whose 'field_archive_date' is NULL OR greater than or equal to a certain date:

        {
          allResults: searchAPISearch(
            index_id: "sitewide"
            range: {start: 0, end: 12}        
            condition_group: {
              conjunction: OR,
              conditions: [
              	{name: "field_archive_date", value: "NULL", operator: "="}
                 {name: "field_archive_date", value: "2019-03-18", operator: ">="}
            	]
            }
            sort: {field: "created" value: "desc"}
            facets: [
              {operator: "=", field: "product", limit: 0, min_count: 0, missing: false}
              {operator: "=", field: "resource_type", limit: 0, min_count: 0, missing: false}
              {operator: "=", field: "segement", limit: 0, min_count: 0, missing: false}
              {operator: "=", field: "solution", limit: 0, min_count: 0, missing: false}
            ]
          ) {
            result_count
            documents {
              ... on SitewideDoc {
                nid
                field_archive_date
              }
            }
            facets{
              name
              values{
                count
                name: filter
              }
            }
          }
        }

Unfortunately, the conditions, when applied in a condition group, are not taking effect. When they are independently applied outside of a condition group, they do apply properly. I've tested this with numerous fields, including default fields like node title, and the conditions do not seem to apply properly in that scenario as well.

Extending the module with Plugins

I got a question, maybe you have a better understanding than me at this point about plugins. I want to override a part of our logic on getting facets back to the user in the response. We are indexing term id's in Solr, and as the module just returns whats currently indexed we only get back term id's, thats not terribly useful.

One thing that came to my mind, is overriding (altering) a certain part of the plugin for example in https://github.com/drupal-graphql/graphql-search-api/blob/8.x-1.x/src/Plugin/GraphQL/Fields/SearchAPISearch.php#L326 where I would basically do a taxonomy term load multiple to get the facets information directly from the query. maybe where we would provide a good old alter hook (? is this still a thing?)

Could also possibly do it here possibbly : https://github.com/drupal-graphql/graphql-search-api/blob/8.x-1.x/src/Plugin/GraphQL/Fields/SearchAPIFacetValues.php#L20 where I maybe would override the whole plugin?

this would allow users to extend our plugins and make their own logic..thinking what should be the module's approach to that in a higher level.

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.