sdkits / examinex Goto Github PK
View Code? Open in Web Editor NEWIssue tracker for ExamineX
Home Page: https://examinex.online
Issue tracker for ExamineX
Home Page: https://examinex.online
ExamineX ensures that rebuilds never occur with an Umbraco replica server since that server is essentially read-only as far as indexing goes. However, when Umbraco cold boots on the primary server, it forces indexes to be rebuilt.
In most cases this is unwanted behavior when an index is centrally hosted like ExamineX.
To replicate, you can delete the ~/App_Data/TEMP/DistCache folder on the primary server which will remove it's last synced ID and force a cold boot on restart.
ExamineX wasn't handling these field types even though they are available types in normal Examine.
This is due to a bug in Examine itself: Shazwazza/Examine#180
The fix in ExamineX will be just to ensure using the 1.0.5 version of Examine.
The IndexModifiedEventType.Updating
is marked Obsolete and is no longer used. This enum is used in the CreatingOrUpdatingIndex
event. IndexModifiedEventType
now contains:
Creating
indicating that a new index is being created.Rebuilding
indicating that an index rebuild is occurring .FieldsChanging
indicating that index field definitions are being added.When using ExamineX with Umbraco in a load balanced scenario, it would be more ideal to not require the front-end (replica) servers to use the Azure Search admin key since these servers do not write or change the index.
This is part of Examine 1.2.0 with a new SkipTake method so will require a min dependency of Examine updated
This is part of Examine 1.1.0 to be able to select specific fields for results. Will require a min dependency update of Examine.
The delay with the Document count value is due to using the azure search 'statistics' to determine this value, however by just using the search service you can get the near real time Document count.
This is now used so the document count should be reflected in near real time. There will still be a message indicating recent indexing activity to note that it may not be real time accurate since we also cache this value for 20 seconds.
This is an issue due to build server obfuscation and can cause the license renewal which occurs with a subscription license to not renew correctly.
To customize an individual index it would be easier to override the Create methods of ExamineXIndexFactory, for example instead of having to override IEnumerable<IIndex> Create()
and return the base implementation of each one if you only wanted to modify the analyzer of one, you could instead do:
protected override IIndex CreateInternalIndex(string defaultAnalyzer)
=> base.CreateInternalIndex(AnalyzerName.AsString.EnMicrosoft);
To create the internal index with a custom analyzer (of course you can then manually also create the index to specify custom fields, etc...)
Also because ExamineX does some index configuration under the hood for replica servers in load balancing, if people override the IEnumerable<IIndex> Create()
they might inadvertently disable this configuration so we need to be able to have a method to ensure that.
The end result will be:
CreateInternalIndex
, CreateExternalIndex
, CreateMemberIndex
OnCreatingIndexes(List<IIndex> indexes)
IEnumerable<IIndex> Create()
is overridden: ConfigureIndexes(IEnumerable<IIndex> indexes)
So the preferred methods to use for customizing the indexes would be to override CreateInternalIndex
, CreateExternalIndex
, or CreateMemberIndex
to customize the built in ones. And/or override OnCreatingIndexes(List<IIndex> indexes)
to add additional custom indexes or replace the existing ones and to not override IEnumerable<IIndex> Create()
Hi Shannon,
we've trying to wrap our head around setting custom analyzers per language (specifying a non default English Lucene analyzer for variants), and doubting whether our current solution is the way to go. Docs are available, but still a bit fuzzy around the "factory" thing you've been mentioning in our "official" email thread.
Current I'm using the .CreatingOrUpdatingIndex
as documented in section "Customizing the Azure Search index" (https://examinex.online/customization) for the external index
private void AzureSearchIndexOnCreatingOrUpdatingIndex(object sender, CreatingOrUpdatingIndexEventArgs e)
{
//var analyzerName = AnalyzerName.EnMicrosoft;
var slovenianAnalyzerName = AnalyzerName.SlMicrosoft;
var slovenianTitleField = e.AzureSearchIndexDefinition.Fields.First(x => x.Name == "title_sl");
slovenianTitleField.Analyzer = slovenianAnalyzerName;
e.AzureSearchIndexDefinition.Fields.First(x => x.Name == "title_sl").Analyzer = AnalyzerName.SlMicrosoft;
}
I'm not 100% sure this is the best way to do it, but from looking at the Azure index in the portal, the analyzer has changed to the Slovenian one.
Doing so, does it have impact on other index field settings? I don't think so, just making sure we're not "corrupting" the default implementation.
Are there better ways to do it? And if so, can you give some extra clues?
Cheers,
Dirk
With the new staging feature it is handy to also show the azure search index name being used for each index in the Examine dashboard.
Currently there is a configuration option to disable all of ExamineX but in some cases it would be beneficial to just disable some ExamineX indexes. For example, if someone is using the free Azure Search tier they may want to disable the Members index if they don't use members to save on an additional index being used. This would already be possible with a bit of code written but might as well make it easier with a couple config values.
A new optional config option will be available so that you can have a staging site up and running with the same license.
These 2 options will provide the flexibility you may require to have staging site indexes in either a separate search service or separate indexes within the same search service depending on your requirements.
<add key="ExamineX.StagingType" value="ByIndex" />
If this config option is applied, then the index names within the same search service will be suffixed with -staging
. For example, for the Internal index with a friendly site name of "My Website", the index name would now be my-website-internal-staging
<add key="ExamineX.StagingType" value="ByService" />
If this config value is applied, then the search service that will be used will be the search service name suffixed with -staging
. For example, if you're search service name is "website-search" then this will configure indexes and the client to use a search service called website-search-staging
NOTE: Any other value specified for this optional app setting will just be treated as if there is no staging configured.
We need to store the CancellationTokenSource.Token reference and use that to pass into any tasks created instead of the CancellationTokenSource.Token which would otherwise cause an ObjectDisposedException when the app is shutting down.
The issue is that Umbraco uses a new examine API to only return specific fields. Lucene doesn't care if you pass it a bunch of fields that don't exist, but Azure Search does. So ExamineX needs to filter out any fields that don't exist in the index when using this new feature.
The error seems to only occur when performing a back office search in Umbraco 8.15 (top right)
Currently we have the ability to have one staging environment as described here https://examinex.online/configuration#staging-sites
which typically allows for a single staging environment since internally the -staging
prefix is hard coded. But we can allow this to be more flexible in cases where there are multiple staging environments like dev
, uat
, etc...
If field limits https://docs.microsoft.com/en-us/azure/search/search-limits-quotas-capacity#index-limits are reached it seems that this isn't handled by exceptions by the azure search client and it fails silently so need to investigate options for notifying users when this occurs.
Examine has IFieldSelectableOrdering which ExamineX needs to implement too.
Change from here https://github.com/umbraco/Umbraco-CMS/pull/9010/files
Possibly need to support this in ExamineX
If ExamineX is installed and Umbraco is in an upgrade state, a YSOD may occur that it cannot find the ExamineComposer
Currently when using the ExamineX.EnvironmentName
, ExamineX will auto-prefix this name with a hyphen. For maximum flexibility we shouldn't do this. If end users wish to have a prefixed hyphen then they can just add it to the config value.
I've got the vast majority of my content items indexed - but certain items yield this message in the logs:
{"@t":"2021-11-04T09:00:05.6396284Z","@mt":"Indexing cancellation requested, cannot
proceed","@l":"Debug","SourceContext":"ExamineX.AzureSearch.AzureSearchIndex","ProcessId":12160,"ProcessName":"w3wp","ThreadId":65,"AppDomainId":5,"AppDomainAppId":"LMW3SVC4ROOT","MachineName":"DESKTOP-L77N24O","Log4NetLevel":"DEBUG"}
Please let me know if I'm able to provide more details!
After upgrading to Umbraco 8.15 (and Examine 1.2), searching in the backoffice with ExamineX installed gives the following error message:
System.NotImplementedException: The method or operation is not implemented.
at Examine.LuceneEngine.Search.LuceneBooleanOperationBase.SelectFields(ISet`1 fieldNames) in C:\Users\Shannon\Documents\_Projects\Examine\Examine\src\Examine\LuceneEngine\Search\LuceneBooleanOperationBase.cs:line 84
at Umbraco.Web.Search.UmbracoTreeSearcher.ExamineSearch(String query, UmbracoEntityTypes entityType, Int32 pageSize, Int64 pageIndex, String culture, Int64& totalFound, String searchFrom, Boolean ignoreUserStartNodes) in D:\a\1\s\src\Umbraco.Web\Search\UmbracoTreeSearcher.cs:line 191
at Umbraco.Web.Trees.ContentTreeController.Search(String query, Int32 pageSize, Int64 pageIndex, Int64& totalFound, String searchFrom) in D:\a\1\s\src\Umbraco.Web\Trees\ContentTreeController.cs:line 331
at Umbraco.Web.Editors.EntityController.SearchAll(String query) in D:\a\1\s\src\Umbraco.Web\Editors\EntityController.cs:line 152
This is probably because these methods were added to LuceneBooleanOperationBase
later and the ExamineX implementation does not implement these methods yet.
Currently to specify custom field types you need to pass those in to the ctor of AzureSearchIndex but this isn't the most developer friendly way to do this since it requires overriding quite a few things.
We need to make it easier for a developer to specify these custom field types. This could be via DI and have them injected per index, or it could be done by passing these customizations in to the Create....
methods (like we do for a custom analyzer).
Shannon,
So we currently have a site in azure the examine setup is as follows:
We have 2 webapps using same db. One app is backoffice only and the other is the frontend.
We currently have 2 issues one is when we deploy on azure via a slot sometimes the custom index fails due to lock. We have to manually delete the lock and search then works again. That custom index is rebuilt on a daily basis at 1am using azure function.
We are also starting to see issue where sometimes for no reason the custom index locks and we have to delete the file again. Hence we wanted new solution where indexes are in the cloud so this looks like a good fit. Looking at my setup I guess I will need to do some rewrite with faceting do you think everything else will work as is or will I need to re write more stuff. I suspect PDF indexing may need some re work?
Thanks
Ismail
Running a NativeQuery with a date range will fail because the dates in the string are not parsed into a range like numerical values.
A work around is to use the strongly typed .RangeQuery<DateTime>
This should work however:
var utcDateRange = searcher.CreateQuery()
.NativeQuery($"created:[{new DateTime(2020, 01, 02).ToUniversalTime():o} TO {new DateTime(2020, 01, 05).ToUniversalTime():o}]");
and even:
var customDateRange = searcher.CreateQuery()
.NativeQuery($"created:[{new DateTime(2020, 01, 02):yyyyMMddHHmm00000} TO {new DateTime(2020, 01, 05):yyyyMMddHHmm00000}]");
After rebuilding the index in the Umbraco back office the total documents indexed will be listed at zero for a few minutes. This is because the data returned from Azure Search is still zero while it processes and re-calculates the total docs. Searching all still works as expected.
Instead of showing 0, the dashboard can show "calculating..." or similar
Umbraco by default dynamically changes all __Raw
types of fields, see https://github.com/umbraco/Umbraco-CMS/blob/v8/contrib/src/Umbraco.Examine/UmbracoExamineIndex.cs#L135
But since that event is specifically for Lucene, Umbraco does not perform this dynamic operation with ExamineX. The ExamineX.Umbraco implementation will need to be updated to make that consistent.
If an index operation was faulted the background task will not get reset so rebuilding index operations will not be processed.
Steps to reproduce:
Expected result:
Documents are crawled and added to the index.
Actual result:
No documents are added to the index.
The following error is shown in the Umbraco logs:
Failed to parse query string at line 1, column 24. See https://aka.ms/azure-search-full-query for supported syntax.
Call Stack:
Microsoft.Rest.Azure.CloudException:
at Microsoft.Azure.Search.DocumentsProxyOperations+d__91.MoveNext (Microsoft.Azure.Search.Data, Version=10.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35) at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089) at Microsoft.Azure.Search.DocumentsProxyOperations+<SearchPostWithHttpMessagesAsync>d__8
1.MoveNext (Microsoft.Azure.Search.Data, Version=10.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
at Microsoft.Azure.Search.DocumentsOperationsExtensions+d__17.MoveNext (Microsoft.Azure.Search.Data, Version=10.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
at Microsoft.Azure.Search.DocumentsOperationsExtensions.Search (Microsoft.Azure.Search.Data, Version=10.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35)
at ExamineX.AzureSearch.AzureSearchResults+C.A (ExamineX.AzureSearch, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null)
at ExamineX.AzureSearch.AzureSearchResults.A (ExamineX.AzureSearch, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null)
The indexes are reporting as healthy. I can see that the indexes themselves were created in the staging search service. See attached screenshots.
ExamineX spawns some background threads to perform some processing and needs to ensure that the execution context is not flowed to these child threads.
Since many web apps don't deploy the app_data folder, the license file should be able to exist in alternative locations:
AzureSearchIndex.DeletedIndex
is a new event raised when an index is deleted which occurs during a rebuild
There are now 2x events that occur when modifying the Azure Search services:
We recently purchased ExamineX license and are having issues connecting to Azure Search Service.
Settings:
Results:
500 Error on attempting to access Examine Management in Back Office
An error occurred The 'ObjectContent 1' type failed to serialize the response body for content type 'application/json; charset=utf-8'. Exception Details System.InvalidOperationException: The 'ObjectContent 1' type failed to serialize the response body for content type 'application/json; charset=utf-8'. Stacktrace Inner Exception System.Xml.XmlException: Root element is missing. at System.Xml.XmlTextReaderImpl.Throw(Exception e) at System.Xml.XmlTextReaderImpl.ParseDocumentContent() at System.Xml.XmlTextReaderImpl.Read() at System.Xml.XmlReader.MoveToContent() at System.Xml.Linq.XElement.Load(XmlReader reader, LoadOptions options) at System.Xml.Linq.XElement.Load(Stream stream, LoadOptions options) at ExamineX.Shared.LicenseManager.a.A() at System.Lazy 1.CreateValue() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Lazy 1.get_Value() at ExamineX.Shared.LicenseManager.a() at System.Lazy 1.CreateValue() at System.Lazy 1.LazyInitValue() at System.Lazy 1.get_Value() at ExamineX.Shared.LicenseManager.IsValid() at ExamineX.AzureSearch.Umbraco.UmbracoAzureSearchIndex.IsHealthy() at Umbraco.Web.Editors.ExamineManagementController.CreateModel(IIndex index) in D:\a\1\s\src\Umbraco.Web\Editors\ExamineManagementController.cs:line 184 at System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext() at System.Linq.Buffer 1..ctor(IEnumerable 1 source) at System.Linq.OrderedEnumerable 1.<GetEnumerator>d__1.MoveNext() at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType) at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType) at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding) at System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding) at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Umbraco.Web.WebApi.AngularJsonMediaTypeFormatter.<WriteToStreamAsync>d__1.MoveNext() in D:\a\1\s\src\Umbraco.Web\WebApi\AngularJsonMediaTypeFormatter.cs:line 52 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__22.MoveNext()
Log Error
Cannot query index, ensure service and credential settings are correct. System.Xml.XmlException: Root element is missing. at System.Xml.XmlTextReaderImpl.Throw(Exception e) at System.Xml.XmlTextReaderImpl.ParseDocumentContent() at System.Xml.XmlTextReaderImpl.Read() at System.Xml.XmlReader.MoveToContent() at System.Xml.Linq.XElement.Load(XmlReader reader, LoadOptions options) at System.Xml.Linq.XElement.Load(Stream stream, LoadOptions options) at ExamineX.Shared.LicenseManager.a.A() at System.Lazy'1.CreateValue() at System.Lazy'1.LazyInitValue() at System.Lazy'1.get_Value() at ExamineX.Shared.LicenseManager.A() at System.Lazy'1.CreateValue() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Lazy'1.get_Value() at ExamineX.Shared.LicenseManager.get_Sku() at ExamineX.Shared.LicenseManagerExtensions.IsStandard(ILicenseManager licenseManager) at A.B.A(ExamineXConfig , String , ILicenseManager ) at ExamineX.AzureSearch.AzureSearchIndex.get_AzureSearchIndexName() at ExamineX.AzureSearch.AzureSearchIndex.A(Lis'1& ) at ExamineX.AzureSearch.AzureSearchIndex.D() at ExamineX.AzureSearch.AzureSearchIndex.a(Boolean ) at ExamineX.AzureSearch.AzureSearchIndex.CreateIndex() at ExamineX.AzureSearch.Umbraco.UmbracoAzureSearchIndex.CreateIndex() at Umbraco.Examine.IndexRebuilder.RebuildIndexes(Boolean onlyEmptyIndexes) in D:\a\1\s\src\Umbraco.Examine\IndexRebuilder.cs:line 58 at Umbraco.Web.Search.BackgroundIndexRebuilder.RebuildOnStartupTask.RebuildIndexes() in D:\a\1\s\src\Umbraco.Web\Search\BackgroundIndexRebuilder.cs:line 119 at Umbraco.Web.Search.BackgroundIndexRebuilder.RebuildOnStartupTask.Run() in D:\a\1\s\src\Umbraco.Web\Search\BackgroundIndexRebuilder.cs:line 93
We frequently run into issues when trying to populate indexes. This problem prevents rebuilding any indexes from the Examine Management dashboard and is indicating that the index has no assigned IIndexPopulator. The image below shows the default internal index being broken, preventing any rebuilds. It should also be noted that node changes are not being handled by Examine anymore.
This issue has proven to be a showstopper on certain machines, while being hard to reproduce on other machines.
Observations
When adding debug logging to the IndexBuilder (assembly: Umbraco.Examine) and monitoring the RebuildIndex action, we noticed that sometimes all injected populators are coming from the ExamineX assembly, and sometimes the injected populators are coming from Umbraco.Examine. The populators dependency doesn't always seem to be resolved properly. When the indexes break, no ExamineX debug logging appears anymore (e.g. queuing of background threads). Disabling ExamineX resolves the populator issue instantly.
Architecture
Our project is on a load balanced server environment. The problem occurs on the master/primary instance.
We are on a mixed index setup where we have multiple custom Azure Search indexes, but keeping default Umbraco indexes on the local file system.
Actions taken
Steps to reproduce
Version info
Any assistance you can provide would be greatly appreciated!
Currently these are applied to all base AzureSearchIndex
which would mean that any index will have the media data source added to it but this should only be enabled for content indexes which are of type UmbracoAzureSearchContentIndex
(i.e. Internal and External)
Currently when an Umbraco cold boot occurs the indexes will be rebuilt as has been the design in Umbraco forever due to the fact that indexes are managed locally. However with a centralized managed index, indexes should not be rebuilt on a cold boot when the server role is a Replica.
The CreatingOrUpdatingIndexEventArgs
contains 1x additional properties: ISearchServiceClient
which is a reference to the Azure Search ISearchServiceClient
API.
There's a hard coded 1000 item limit in Azure Search which will not allow returning more than 1000 items in one page. This would be a fairly rare requirement but it can be fixed to allow it by doing the required paging HTTP requests behind the scenes.
The work around for now if you require more than 1000 search results is to page through results in smaller page sizes (the default is max 500).
Instead of throwing an exception if the license file is invalid and cannot be read, the license system should just mark it as an invalid license and display that message accordingly.
Root element is missing. at System.Xml.XmlTextReaderImpl.Throw(Exception e) at System.Xml.XmlTextReaderImpl.ParseDocumentContent() at System.Xml.XmlTextReaderImpl.Read() at System.Xml.XmlReader.MoveToContent() at System.Xml.Linq.XElement.Load(XmlReader reader, LoadOptions options) at System.Xml.Linq.XElement.Load(Stream stream, LoadOptions options) at ExamineX.Shared.LicenseManager.a.A() at System.Lazy 1.CreateValue() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Lazy 1.get_Value() at ExamineX.Shared.LicenseManager.a() at System.Lazy 1.CreateValue() at System.Lazy 1.LazyInitValue() at System.Lazy 1.get_Value() at ExamineX.Shared.LicenseManager.IsValid() at
See question #32
Show index and search result limits based on the current ExamineX license state in Examine management dashboard
The exception will only occur in some cases when some libraries call into a specific overload of Type.GetType. This was discovered when using the redis session state provider which does exactly that.
The cause is due to some post processing during the compiling of ExamineX assemblies. It's not an actual issue with the ExamineX code.
Instead of the configuration values "ExamineX.StagingType" it would be "ExamineX.EnvironmentType" and instead of "ExamineX.StagingName" it would be "ExamineX.EnvironmentName"
This is to ensure that people don't think that this option pertains only to pre-production environments since these options can in some cases be used in production environments.
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.