From 949a550ae92f322cba1b571cdebf49aad8854a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Ros?= Date: Thu, 7 Nov 2024 10:35:44 -0800 Subject: [PATCH 1/3] Add random() order support (#16964) Co-authored-by: Hisham Bin Ateya --- .../OrchardCore.Queries/Sql/SqlGrammar.cs | 2 +- .../OrchardCore.Queries/Sql/SqlParser.cs | 27 +++++++++++++++---- src/docs/reference/modules/Queries/README.md | 6 +++++ .../Orchard.Queries/SqlParserTests.cs | 15 +++++++++++ 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlGrammar.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlGrammar.cs index 319b7a8d4b6..5d0ab6b3ace 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlGrammar.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlGrammar.cs @@ -146,7 +146,7 @@ public SqlGrammar() : base(false) // Create Index. orderList.Rule = MakePlusRule(orderList, comma, orderMember); - orderMember.Rule = Id + orderDirOptional; + orderMember.Rule = Id + (orderDirOptional | "(" + functionArguments + ")"); orderDirOptional.Rule = Empty | "ASC" | "DESC"; // Select stmt. diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParser.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParser.cs index 9c0021377cc..d397404352d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParser.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParser.cs @@ -246,20 +246,37 @@ private void EvaluateOrderClause(ParseTreeNode parseTreeNode) var idList = parseTreeNode.ChildNodes[2]; _modes.Push(FormattingModes.SelectClause); + for (var i = 0; i < idList.ChildNodes.Count; i++) { - var id = idList.ChildNodes[i].ChildNodes[0]; - if (i > 0) { _builder.Append(", "); } + var id = idList.ChildNodes[i].ChildNodes[0]; + + // RANDOM() is a special case where we need to use the dialect's random function. + if (id.ChildNodes[0].Token != null && id.ChildNodes[0].Token.ValueString.Equals("RANDOM", StringComparison.OrdinalIgnoreCase)) + { + var funArgs = idList.ChildNodes[i].ChildNodes[1].ChildNodes[0]; + + // "RANDOM" + {funArgs} + no arguments? + if (funArgs.Term.Name == "funArgs" && funArgs.ChildNodes.Count == 0) + { + _builder.Append(_dialect.RandomOrderByClause); + + continue; + } + } + EvaluateId(id); - if (idList.ChildNodes[i].ChildNodes[1].ChildNodes.Count > 0) + var orderDirOpt = idList.ChildNodes[i].ChildNodes[1].ChildNodes[0]; + + if (orderDirOpt.Term.Name == "orderDirOpt" && orderDirOpt.ChildNodes.Count > 0) { - _builder.Append(' ').Append(idList.ChildNodes[i].ChildNodes[1].ChildNodes[0].Term.Name); + _builder.Append(' ').Append(orderDirOpt.ChildNodes[0].Term.Name); } } @@ -805,7 +822,7 @@ private void EvaluateOverClauseOptional(ParseTreeNode overClauseOpt) var orderMember = orderList.ChildNodes[i]; var id = orderMember.ChildNodes[0]; EvaluateSelectId(id); - var orderDirOpt = orderMember.ChildNodes[1]; + var orderDirOpt = orderMember.ChildNodes[1].ChildNodes[0]; if (orderDirOpt.ChildNodes.Count > 0) { _builder.Append(' ').Append(orderDirOpt.ChildNodes[0].Term.Name); diff --git a/src/docs/reference/modules/Queries/README.md b/src/docs/reference/modules/Queries/README.md index 8b41a04c54b..c7e4dc5c441 100644 --- a/src/docs/reference/modules/Queries/README.md +++ b/src/docs/reference/modules/Queries/README.md @@ -289,6 +289,12 @@ The SQL parser is also able to convert some specific functions to the intended d | `year(_date_)` | Returns the years part of a date. | | `now()` | Returns current date time (utc). | +Order By clauses can also use the `random()` function (case insensitive) to order results randomly, e.g., + +``` +SELECT * FROM ContentItemIndex ORDER BY random() +``` + ## Scripting The following JavaScript functions are available with this module. diff --git a/test/OrchardCore.Tests/Orchard.Queries/SqlParserTests.cs b/test/OrchardCore.Tests/Orchard.Queries/SqlParserTests.cs index a7f392fe2c2..ec7850a5479 100644 --- a/test/OrchardCore.Tests/Orchard.Queries/SqlParserTests.cs +++ b/test/OrchardCore.Tests/Orchard.Queries/SqlParserTests.cs @@ -243,4 +243,19 @@ public void ShouldParseSubquery(string sql, string expectedSql) Assert.True(result); Assert.Equal(expectedSql, FormatSql(rawQuery)); } + + [Theory] + [InlineData("select a order by RANDOM()", "SELECT [a] ORDER BY newid();")] + [InlineData("select a order by random()", "SELECT [a] ORDER BY newid();")] + [InlineData("select a order by RANDOM", "SELECT [a] ORDER BY [RANDOM];")] + [InlineData("select a order by random", "SELECT [a] ORDER BY [random];")] + public void ShouldOrderByRandom(string sql, string expectedSql) + { + // Arrange & Act + var result = SqlParser.TryParse(sql, _schema, _defaultDialect, _defaultTablePrefix, null, out var rawQuery, out var errors); + + // Assert + Assert.True(result); + Assert.Equal(expectedSql, FormatSql(rawQuery)); + } } From d08a99d0dbe42722532ac75057312401f863d2ee Mon Sep 17 00:00:00 2001 From: Emrah Tokalak Date: Thu, 7 Nov 2024 21:37:48 +0300 Subject: [PATCH 2/3] Fix RangeQueryProvider to correctly assign nodeKind for 'lt' and 'lte' cases (#16965) Co-authored-by: emrah.tokalak --- .../QueryProviders/RangeQueryProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OrchardCore/OrchardCore.Search.Lucene.Core/QueryProviders/RangeQueryProvider.cs b/src/OrchardCore/OrchardCore.Search.Lucene.Core/QueryProviders/RangeQueryProvider.cs index 6bcb9d365ca..89eef231d52 100644 --- a/src/OrchardCore/OrchardCore.Search.Lucene.Core/QueryProviders/RangeQueryProvider.cs +++ b/src/OrchardCore/OrchardCore.Search.Lucene.Core/QueryProviders/RangeQueryProvider.cs @@ -43,11 +43,11 @@ public Query CreateQuery(ILuceneQueryService builder, LuceneQueryContext context break; case "lt": lt = element.Value; - nodeKind = gt.GetValueKind(); + nodeKind = lt.GetValueKind(); break; case "lte": lt = element.Value; - nodeKind = gt.GetValueKind(); + nodeKind = lt.GetValueKind(); includeUpper = true; break; case "boost": From 5a14fb74524f6c6f8358a6326bf9c946294d609a Mon Sep 17 00:00:00 2001 From: Hisham Bin Ateya Date: Thu, 7 Nov 2024 21:52:08 +0300 Subject: [PATCH 3/3] Extract PDF on file system instead of memory (#16958) --------- Co-authored-by: Mike Alhayek --- .../Services/PdfMediaFileTextProvider.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs b/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs index 11f1bd98dac..3cb4917a52e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs @@ -11,16 +11,15 @@ public async Task GetTextAsync(string path, Stream fileStream) // https://github.com/UglyToad/PdfPig/blob/master/src/UglyToad.PdfPig.Core/StreamInputBytes.cs#L45. // Thus if it isn't, which is the case with e.g. Azure Blob Storage, we need to copy it to a new, seekable // Stream. - MemoryStream seekableStream = null; + FileStream seekableStream = null; try { if (!fileStream.CanSeek) { - // Since fileStream.Length might not be supported either, we can't preconfigure the capacity of the - // MemoryStream. - seekableStream = new MemoryStream(); - // While this involves loading the file into memory, we don't really have a choice. + seekableStream = new FileStream(Path.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, 4096, FileOptions.DeleteOnClose); + await fileStream.CopyToAsync(seekableStream); + seekableStream.Position = 0; }