Ejemplo n.º 1
0
        public async Task <(bool success, IReadOnlyCollection <object> results)> RunAsync(IDocumentStore documentStore, Connection connection, QueryParts queryParts, bool logStats, ILogger logger, CancellationToken cancellationToken, Dictionary <string, IReadOnlyCollection <object> > variables = null)
        {
            try
            {
                if (!queryParts.IsValidQuery())
                {
                    logger.LogError("Invalid Query. Aborting Delete.");
                    return(false, null);
                }

                //get the ids
                var selectQuery = queryParts.ToRawSelectQuery();
                var results     = await documentStore.ExecuteAsync(connection.Database, queryParts.CollectionName,
                                                                   async (IDocumentExecuteContext context) =>
                {
                    var queryOptions = new QueryOptions
                    {
                        PopulateQueryMetrics      = true,
                        EnableCrossPartitionQuery = true,
                        MaxBufferedItemCount      = 200,
                        MaxDegreeOfParallelism    = MAX_DEGREE_PARALLEL,
                        MaxItemCount = -1,
                    };
                    if (variables != null && variables.Any() && queryParts.HasVariablesInWhereClause())
                    {
                        selectQuery = _variableInjectionTask.InjectVariables(selectQuery, variables, logger);
                    }
                    var query = context.QueryAsSql <object>(selectQuery, queryOptions);
                    return(await query.ConvertAndLogRequestUnits(false, logger));
                }, cancellationToken);

                var fromObjects = JArray.FromObject(results);
                if (queryParts.IsTransaction)
                {
                    logger.LogInformation($"Transaction Created. TransactionId: {queryParts.TransactionId}");
                    await _transactionTask.BackuQueryAsync(connection.Name, connection.Database, queryParts.CollectionName, queryParts.TransactionId, queryParts.CleanOrginalQuery);
                }
                var partitionKeyPath = await documentStore.LookupPartitionKeyPath(connection.Database, queryParts.CollectionName);

                var deleteCount = 0;
                var actionTransactionCacheBlock = new ActionBlock <JObject>(async document =>
                {
                    await documentStore.ExecuteAsync(connection.Database, queryParts.CollectionName,
                                                     async(IDocumentExecuteContext context) =>
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            throw new TaskCanceledException("Task has been requested to cancel.");
                        }
                        var documentId = document[Constants.DocumentFields.ID].ToString();

                        if (queryParts.IsTransaction)
                        {
                            var backupResult = await _transactionTask.BackupAsync(context, connection.Name, connection.Database, queryParts.CollectionName, queryParts.TransactionId, logger, null, document);
                            if (!backupResult.isSuccess)
                            {
                                logger.LogError($"Unable to backup document {documentId}. Skipping Delete.");
                                return(false);
                            }
                        }

                        var partionKeyValue = document.SelectToken(partitionKeyPath).ToString();
                        var deleted         = await context.DeleteAsync(documentId.CleanId(), new RequestOptions
                        {
                            PartitionKey = partionKeyValue
                        });
                        if (deleted)
                        {
                            Interlocked.Increment(ref deleteCount);
                            logger.LogInformation($"Deleted {documentId}");
                        }
                        else
                        {
                            logger.LogInformation($"Document {documentId} unable to be deleted.");
                        }
                        return(true);
                    }, cancellationToken);
                },
                                                                            new ExecutionDataflowBlockOptions
                {
                    MaxDegreeOfParallelism = MAX_DEGREE_PARALLEL,
                    CancellationToken      = cancellationToken
                });

                foreach (JObject doc in fromObjects)
                {
                    actionTransactionCacheBlock.Post(doc);
                }
                actionTransactionCacheBlock.Complete();
                await actionTransactionCacheBlock.Completion;
                logger.LogInformation($"Deleted {deleteCount} out of {fromObjects.Count}");
                if (queryParts.IsTransaction && deleteCount > 0)
                {
                    logger.LogInformation($"To rollback execute: ROLLBACK {queryParts.TransactionId}");
                }
                return(true, null);
            }
            catch (Exception ex)
            {
                logger.Log(LogLevel.Error, new EventId(), $"Unable to run {Constants.QueryParsingKeywords.DELETE} query", ex);
                return(false, null);
            }
        }
        public async Task <(bool success, IReadOnlyCollection <object> results)> RunAsync(IDocumentStore documentStore, Connection connection, QueryParts queryParts, bool logStats, ILogger logger, CancellationToken cancellationToken, Dictionary <string, IReadOnlyCollection <object> > variables = null)
        {
            try
            {
                if (!queryParts.IsValidQuery())
                {
                    logger.LogError("Invalid Query. Aborting Delete.");
                    return(false, null);
                }

                var ids = queryParts.CleanQueryBody.Split(new[] { ',' });

                if (queryParts.IsTransaction)
                {
                    logger.LogInformation($"Transaction Created. TransactionId: {queryParts.TransactionId}");
                    await _transactionTask.BackuQueryAsync(connection.Name, connection.Database, queryParts.CollectionName, queryParts.TransactionId, queryParts.CleanOrginalQuery);
                }
                var partitionKeyPath = await documentStore.LookupPartitionKeyPath(connection.Database, queryParts.CollectionName);

                var deleteCount = 0;
                var actionTransactionCacheBlock = new ActionBlock <string>(async documentId =>
                {
                    //this handles transaction saving for recovery
                    await documentStore.ExecuteAsync(connection.Database, queryParts.CollectionName,
                                                     async(IDocumentExecuteContext context) =>
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            throw new TaskCanceledException("Task has been requested to cancel.");
                        }
                        if (queryParts.IsTransaction)
                        {
                            var backupResult = await _transactionTask.BackupAsync(context, connection.Name, connection.Database, queryParts.CollectionName, queryParts.TransactionId, logger, documentId);
                            if (!backupResult.isSuccess)
                            {
                                logger.LogError($"Unable to backup document {documentId}. Skipping Delete.");
                                return(false);
                            }
                        }

                        var queryToFindOptions = new QueryOptions
                        {
                            PopulateQueryMetrics      = false,
                            EnableCrossPartitionQuery = true,
                            MaxItemCount = 1,
                        };
                        //we have to query to find the partitionKey value so we can do the delete
                        var queryToFind        = context.QueryAsSql <object>($"SELECT {queryParts.CollectionName}.{partitionKeyPath} FROM {queryParts.CollectionName} WHERE {queryParts.CollectionName}.id = '{documentId.CleanId()}'", queryToFindOptions);
                        var partitionKeyResult = (await queryToFind.ConvertAndLogRequestUnits(false, logger)).FirstOrDefault();

                        if (partitionKeyResult != null)
                        {
                            var jobj            = JObject.FromObject(partitionKeyResult);
                            var partionKeyValue = jobj.SelectToken(partitionKeyPath).ToString();

                            var deleted = await context.DeleteAsync(documentId.CleanId(), new RequestOptions
                            {
                                PartitionKey = partionKeyValue
                            });
                            if (deleted)
                            {
                                Interlocked.Increment(ref deleteCount);
                                logger.LogInformation($"Deleted {documentId}");
                            }
                            else
                            {
                                logger.LogInformation($"Document {documentId} unable to be deleted.");
                            }
                        }
                        else
                        {
                            logger.LogInformation($"Document {documentId} not found. Skipping");
                        }
                        return(true);
                    }, cancellationToken);
                },
                                                                           new ExecutionDataflowBlockOptions
                {
                    MaxDegreeOfParallelism = MAX_DEGREE_PARALLEL,
                    CancellationToken      = cancellationToken
                });

                foreach (var id in ids)
                {
                    actionTransactionCacheBlock.Post(id);
                }
                actionTransactionCacheBlock.Complete();
                await actionTransactionCacheBlock.Completion;
                logger.LogInformation($"Deleted {deleteCount} out of {ids.Length}");
                if (queryParts.IsTransaction && deleteCount > 0)
                {
                    logger.LogInformation($"To rollback execute: ROLLBACK {queryParts.TransactionId}");
                }
                return(true, null);
            }
            catch (Exception ex)
            {
                logger.Log(LogLevel.Error, new EventId(), $"Unable to run {Constants.QueryParsingKeywords.DELETE} query", ex);
                return(false, null);
            }
        }
Ejemplo n.º 3
0
        public async Task <(bool success, IReadOnlyCollection <object> results)> RunAsync(IDocumentStore documentStore, Connection connection, QueryParts queryParts, bool logStats, ILogger logger, CancellationToken cancellationToken, Dictionary <string, IReadOnlyCollection <object> > variables = null)
        {
            try
            {
                if (!queryParts.IsValidQuery())
                {
                    logger.LogError("Invalid Query. Aborting Update.");
                    return(false, null);
                }

                var ids = queryParts.CleanQueryBody.Split(new[] { ',' });

                if (queryParts.CleanQueryUpdateType == Constants.QueryParsingKeywords.REPLACE && ids.Length > 1)
                {
                    var errorMessage = $"{Constants.QueryParsingKeywords.REPLACE} only supports replacing 1 document at a time.";
                    logger.LogError(errorMessage);
                    return(false, null);
                }

                if (queryParts.IsTransaction)
                {
                    logger.LogInformation($"Transaction Created. TransactionId: {queryParts.TransactionId}");
                    await _transactionTask.BackuQueryAsync(connection.Name, connection.Database, queryParts.CollectionName, queryParts.TransactionId, queryParts.CleanOrginalQuery);
                }
                var partitionKeyPath = await documentStore.LookupPartitionKeyPath(connection.Database, queryParts.CollectionName);

                var updateCount = 0;
                var actionTransactionCacheBlock = new ActionBlock <string>(async documentId =>
                {
                    //this handles transaction saving for recovery
                    await documentStore.ExecuteAsync(connection.Database, queryParts.CollectionName,
                                                     async(IDocumentExecuteContext context) =>
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            throw new TaskCanceledException("Task has been requested to cancel.");
                        }
                        JObject jDoc = null;
                        if (queryParts.IsTransaction)
                        {
                            var backupResult = await _transactionTask.BackupAsync(context, connection.Name, connection.Database, queryParts.CollectionName, queryParts.TransactionId, logger, documentId);
                            if (!backupResult.isSuccess)
                            {
                                logger.LogError($"Unable to backup document {documentId}. Skipping Update.");
                                return(false);
                            }
                            jDoc = backupResult.document;
                        }

                        if (queryParts.IsReplaceUpdateQuery())
                        {
                            var fullJObjectToUpdate        = JObject.Parse(queryParts.CleanQueryUpdateBody);
                            var fullJObjectPartionKeyValue = fullJObjectToUpdate.SelectToken(partitionKeyPath).ToString();
                            var fullJObjectUpdatedDoc      = await context.UpdateAsync(fullJObjectToUpdate, new RequestOptions
                            {
                                PartitionKey = fullJObjectPartionKeyValue
                            });
                            if (fullJObjectUpdatedDoc != null)
                            {
                                Interlocked.Increment(ref updateCount);
                                logger.LogInformation($"Updated {documentId}");
                            }
                            else
                            {
                                logger.LogInformation($"Document {documentId} unable to be updated.");
                            }
                            return(true);
                        }

                        //this is a partial update
                        if (jDoc == null)
                        {
                            //this would only need to run if not in a transaction, because in a transaction we have already queried for the doc and have it.
                            var queryToFindOptions = new QueryOptions
                            {
                                PopulateQueryMetrics      = false,
                                EnableCrossPartitionQuery = true,
                                MaxItemCount = 1,
                            };
                            //we have to query to find the partitionKey value so we can do the delete
                            var queryToFind    = context.QueryAsSql <object>($"SELECT * FROM {queryParts.CollectionName} WHERE {queryParts.CollectionName}.id = '{documentId.CleanId()}'", queryToFindOptions);
                            var queryResultDoc = (await queryToFind.ConvertAndLogRequestUnits(false, logger)).FirstOrDefault();
                            if (queryResultDoc == null)
                            {
                                logger.LogInformation($"Document {documentId} not found. Skipping Update");
                                return(false);
                            }
                            jDoc = JObject.FromObject(queryResultDoc);
                        }
                        var partionKeyValue = jDoc.SelectToken(partitionKeyPath).ToString();

                        var partialDoc = JObject.Parse(queryParts.CleanQueryUpdateBody);

                        //ensure the partial update is not trying to update id or the partition key
                        var pToken  = partialDoc.SelectToken(partitionKeyPath);
                        var idToken = partialDoc.SelectToken(Constants.DocumentFields.ID);
                        if (pToken != null || idToken != null)
                        {
                            logger.LogError($"Updates are not allowed on ids or existing partition keys of a document. Skipping updated for document {documentId}.");
                            return(false);
                        }
                        var shouldUpdateToEmptyArray = partialDoc.HasEmptyJArray();
                        jDoc.Merge(partialDoc, new JsonMergeSettings
                        {
                            MergeArrayHandling     = shouldUpdateToEmptyArray ? MergeArrayHandling.Replace : MergeArrayHandling.Merge,
                            MergeNullValueHandling = MergeNullValueHandling.Merge
                        });

                        //save
                        var updatedDoc = await context.UpdateAsync(jDoc, new RequestOptions
                        {
                            PartitionKey = partionKeyValue
                        });
                        if (updatedDoc != null)
                        {
                            Interlocked.Increment(ref updateCount);
                            logger.LogInformation($"Updated {documentId}");
                        }
                        else
                        {
                            logger.LogInformation($"Document {documentId} unable to be updated.");
                        }

                        return(true);
                    }, cancellationToken);
                },
                                                                           new ExecutionDataflowBlockOptions
                {
                    MaxDegreeOfParallelism = MAX_DEGREE_PARALLEL,
                    CancellationToken      = cancellationToken
                });

                foreach (var id in ids)
                {
                    actionTransactionCacheBlock.Post(id);
                }
                actionTransactionCacheBlock.Complete();
                await actionTransactionCacheBlock.Completion;
                logger.LogInformation($"Updated {updateCount} out of {ids.Length}");
                if (queryParts.IsTransaction && updateCount > 0)
                {
                    logger.LogInformation($"To rollback execute: ROLLBACK {queryParts.TransactionId}");
                }
                return(true, null);
            }
            catch (Exception ex)
            {
                var errorMessage = $"Unable to run {Constants.QueryParsingKeywords.UPDATE} query.";
                if (queryParts.CleanQueryUpdateType == Constants.QueryParsingKeywords.REPLACE)
                {
                    errorMessage += $"{Constants.QueryParsingKeywords.REPLACE} only supports replacing 1 document at a time.";
                }
                logger.Log(LogLevel.Error, new EventId(), errorMessage, ex);
                return(false, null);
            }
        }
        public async Task <(bool success, IReadOnlyCollection <object> results)> RunAsync(IDocumentStore documentStore, Connection connection, QueryParts queryParts, bool logStats, ILogger logger, CancellationToken cancellationToken, Dictionary <string, IReadOnlyCollection <object> > variables = null)
        {
            try
            {
                if (!queryParts.IsValidQuery())
                {
                    logger.LogError("Invalid Query. Aborting Update.");
                    return(false, null);
                }

                if (queryParts.IsReplaceUpdateQuery())
                {
                    logger.LogError($"Full document updating not supported in SELECT/WHERE queries. To update those documents use an update by documentId query. Skipping Update.");
                    return(false, null);
                }
                //get the ids
                var selectQuery = queryParts.ToRawSelectQuery();
                var results     = await documentStore.ExecuteAsync(connection.Database, queryParts.CollectionName,
                                                                   async (IDocumentExecuteContext context) =>
                {
                    var queryOptions = new QueryOptions
                    {
                        PopulateQueryMetrics      = true,
                        EnableCrossPartitionQuery = true,
                        MaxBufferedItemCount      = 200,
                        MaxDegreeOfParallelism    = MAX_DEGREE_PARALLEL,
                        MaxItemCount = -1,
                    };

                    if (variables != null && variables.Any() && queryParts.HasVariablesInWhereClause())
                    {
                        selectQuery = _variableInjectionTask.InjectVariables(selectQuery, variables, logger);
                    }

                    var query = context.QueryAsSql <object>(selectQuery, queryOptions);
                    return(await query.ConvertAndLogRequestUnits(false, logger));
                }, cancellationToken);

                var fromObjects = JArray.FromObject(results);
                if (queryParts.IsTransaction)
                {
                    logger.LogInformation($"Transaction Created. TransactionId: {queryParts.TransactionId}");
                    await _transactionTask.BackuQueryAsync(connection.Name, connection.Database, queryParts.CollectionName, queryParts.TransactionId, queryParts.CleanOrginalQuery);
                }
                var partitionKeyPath = await documentStore.LookupPartitionKeyPath(connection.Database, queryParts.CollectionName);

                var updateCount = 0;
                var actionTransactionCacheBlock = new ActionBlock <JObject>(async document =>
                {
                    await documentStore.ExecuteAsync(connection.Database, queryParts.CollectionName,
                                                     async(IDocumentExecuteContext context) =>
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            throw new TaskCanceledException("Task has been requested to cancel.");
                        }
                        var documentId = document[Constants.DocumentFields.ID].ToString();

                        if (queryParts.IsTransaction)
                        {
                            var backupResult = await _transactionTask.BackupAsync(context, connection.Name, connection.Database, queryParts.CollectionName, queryParts.TransactionId, logger, null, document);
                            if (!backupResult.isSuccess)
                            {
                                logger.LogError($"Unable to backup document {documentId}. Skipping Update.");
                                return(false);
                            }
                        }

                        var partionKeyValue = document.SelectToken(partitionKeyPath).ToString();

                        var partialDoc = JObject.Parse(queryParts.CleanQueryUpdateBody);
                        //ensure the partial update is not trying to update id or the partition key
                        var pToken  = partialDoc.SelectToken(partitionKeyPath);
                        var idToken = partialDoc.SelectToken(Constants.DocumentFields.ID);
                        if (pToken != null || idToken != null)
                        {
                            logger.LogError($"Updates are not allowed on ids or existing partition keys of a document. Skipping updated for document {documentId}.");
                            return(false);
                        }
                        var shouldUpdateToEmptyArray = partialDoc.HasEmptyJArray();
                        document.Merge(partialDoc, new JsonMergeSettings
                        {
                            MergeArrayHandling     = shouldUpdateToEmptyArray ? MergeArrayHandling.Replace : MergeArrayHandling.Merge,
                            MergeNullValueHandling = MergeNullValueHandling.Merge
                        });

                        //save
                        var updatedDoc = await context.UpdateAsync(document, new RequestOptions
                        {
                            PartitionKey = partionKeyValue
                        });
                        if (updatedDoc != null)
                        {
                            Interlocked.Increment(ref updateCount);
                            logger.LogInformation($"Updated {documentId}");
                        }
                        else
                        {
                            logger.LogInformation($"Document {documentId} unable to be updated.");
                        }
                        return(true);
                    }, cancellationToken);
                },
                                                                            new ExecutionDataflowBlockOptions
                {
                    MaxDegreeOfParallelism = MAX_DEGREE_PARALLEL,
                    CancellationToken      = cancellationToken
                });

                foreach (JObject doc in fromObjects)
                {
                    actionTransactionCacheBlock.Post(doc);
                }
                actionTransactionCacheBlock.Complete();
                await actionTransactionCacheBlock.Completion;
                logger.LogInformation($"Updated {updateCount} out of {fromObjects.Count}");
                if (queryParts.IsTransaction && updateCount > 0)
                {
                    logger.LogInformation($"To rollback execute: ROLLBACK {queryParts.TransactionId}");
                }
                return(true, null);
            }
            catch (Exception ex)
            {
                logger.Log(LogLevel.Error, new EventId(), $"Unable to run {Constants.QueryParsingKeywords.DELETE} query", ex);
                return(false, null);
            }
        }