Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOCSP-34044: Add bitwise operators to aggregation pipeline #253

Merged
merged 23 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 168 additions & 4 deletions source/fundamentals/linq.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LINQ
.. contents:: On this page
:local:
:backlinks: none
:depth: 2
:depth: 3
:class: singlecol

.. facet::
Expand Down Expand Up @@ -68,10 +68,11 @@ object that links to the collection. To create the object, use the ``AsQueryable
as follows:

.. code-block:: csharp
:emphasize-lines: 2
:emphasize-lines: 3

var restaurantsCollection = restaurantsDatabase.GetCollection<Restaurant>("restaurants");
var queryableCollection = restaurantsCollection.AsQueryable();
var restaurantsDatabase = client.GetDatabase("sample_restaurants");
var restaurantsCollection = restaurantsDatabase.GetCollection<Restaurant>("restaurants");
var queryableCollection = restaurantsCollection.AsQueryable();

The ``AsQueryable()`` method returns an `IMongoQueryable
<{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.Linq.IMongoQueryable.html>`__ instance that
Expand Down Expand Up @@ -587,6 +588,169 @@ in the Atlas manual. For more examples about running Atlas Vector Search queries
{+driver-short+}, see :atlas:`Run Vector Search Queries </atlas-vector-search/vector-search-stage/>`
in the Atlas manual and select :guilabel:`C#` from the language dropdown.

Bitwise Operators
~~~~~~~~~~~~~~~~~

This section describes the :wikipedia:`bitwise operators <Bitwise_operation>`
supported by the {+driver-short+} that you can use in an aggregation pipeline.
You can use multiple bitwise operators in the same
stage. The following guidelines apply when using bitwise operators:

- All operands must be of type ``int`` or ``long``.

- ``$bitAnd``, ``$bitOr``, and ``$bitXor`` take two or more operands. ``$bitNot`` takes one operand.

- Bitwise operations are evaluated from left to right.

The examples in this section use the following documents in a collection called
``ingredients``:

.. code-block:: json

{ "_id": 1, "name": "watermelon", "price": 5, "count": 1 },
{ "_id": 2, "name": "onions", "price": 2, "count": 4 },
{ "_id": 3, "name": "eggs", "price": 5, "count": 12 },
{ "_id": 4, "name": "potatoes", "price": 3, "count": 0 },
{ "_id": 5, "name": "pasta", "price": 4, "count": 100 },
{ "_id": 6, "name": "cheese", "price": 4 }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This final document can't have been created using the Ingredient POCO because Count is an int and can't be null.

Also, the element names in these sample documents don't match the field names in the POCO (the C# properties start with a capital letter).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data types used in the example have been changed to be nullable.

As for the second point, for these examples we use a convention pack (see the bottom of the Overview section on this page) to convert snake case field names into Pascal case.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to tell from looking at just this PR that there is a convention somewhere else that is mapping the property names. That's confusing.


The following ``Ingredient`` class models the documents in the ``ingredients``
collection:

.. literalinclude:: /includes/fundamentals/code-examples/linq.cs
:language: csharp
:dedent:
:start-after: start-ingredient-model
:end-before: end-ingredient-model

.. note:: Missing or Undefined Operands

If the operand list you pass to any bitwise operator contains a missing or
undefined value, the entire expression evaluates to ``null``.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is true server-side, but in C# this isn't true unless you are using "nullable" integers.

In these examples if either Price or Count is null the query will fail client-side while deserializing the results.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified the note, please take a look!


$bitAnd
+++++++

The ``$bitAnd`` aggregation operator performs a bitwise AND operation on the given
arguments. You can use the ``$bitAnd`` operator by connecting two or more
clauses with a ``&`` character.

The following example shows how to create a ``$bitAnd`` stage by using LINQ. The
code retrieves the document in which the ``Name`` field has the
value ``"eggs"``. It then performs a bitwise AND operation on the values of the
``Price`` and ``Count`` fields in this document.

.. literalinclude:: /includes/fundamentals/code-examples/linq.cs
:language: csharp
:dedent:
:start-after: start-bitAnd-example
:end-before: end-bitAnd-example

The preceding code returns ``4``, the result of the AND operation on the values
of the ``Price`` field (``5``) and the ``Count`` field (``12``).

The following example performs the same bitwise AND operation on all
documents in the collection:

.. io-code-block::
:copyable: true

.. input:: /includes/fundamentals/code-examples/linq.cs
:language: csharp
:dedent:
:start-after: start-bitAnd-collection-example
:end-before: end-bitAnd-collection-example

.. output::
:language: json
:visible: false

1
0
4
0
4
null

The ``null`` result comes from the document where the ``Name`` field
has the value of ``"cheese"``. This document is missing a ``Count`` field, so
the expression evaluates to ``null``.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried to execute this?

I think it will throw an exception because the null can't be deserialized to an int client-side.

Copy link
Collaborator Author

@mayaraman19 mayaraman19 Oct 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All examples in this PR have been executed - although, the issue is that I was using nullable integers but did not add that in the example. They have now been added.


$bitOr
++++++

The ``$bitOr`` aggregation operator performs a bitwise OR operation on the given
arguments. You can use the ``$bitOr`` operator by connecting two or more
clauses with a ``|`` character.

The following example shows how to create a ``$bitOr`` stage by using LINQ. The
code retrieves the document in which the ``Name`` field has the
value ``"eggs"``. It then performs a bitwise OR operation on the values of the
``Price`` and ``Count`` fields in this document.

.. literalinclude:: /includes/fundamentals/code-examples/linq.cs
:language: csharp
:dedent:
:start-after: start-bitOr-example
:end-before: end-bitOr-example

The preceding code returns ``13``, the result of the OR operation on the values
of the ``Price`` field (``5``) and the ``Count`` field (``12``).

$bitNot
+++++++

The ``$bitNot`` aggregation operator performs a bitwise NOT operation on the given
argument. You can use the ``$bitNot`` operator by preceding an
operand with a ``~`` character. ``$bitNot`` only takes one argument. The
following example shows how to create a ``$bitNot`` stage by using LINQ:

.. io-code-block::
:copyable: true

.. input:: /includes/fundamentals/code-examples/linq.cs
:language: csharp
:dedent:
:start-after: start-bitNot-example
:end-before: end-bitNot-example

.. output::
:language: json
:visible: false

-2
-5
-13
-5
-101
null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null is not a possible result. It should throw an exception when deserializing the results.


$bitXor
+++++++

The ``$bitXor`` aggregation operator performs a bitwise XOR operation on the given
arguments. You can use the ``$bitXor`` operator by connecting two or more
clauses with a ``^`` character.

The following example shows how to create a ``$bitXor`` stage by using LINQ. The
code retrieves the documents in which the ``Name`` field has
the value ``"eggs"`` or ``"watermelon"``. It then performs a bitwise XOR
operation on the values of the ``Price`` and ``Count`` fields in these
documents.

.. literalinclude:: /includes/fundamentals/code-examples/linq.cs
:language: csharp
:dedent:
:start-after: start-bitXor-example
:end-before: end-bitXor-example

The result contains the following values:

.. code-block:: json

4
9

Unsupported Aggregation Stages
------------------------------

Expand Down
55 changes: 54 additions & 1 deletion source/includes/fundamentals/code-examples/linq.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class Address
public string Building { get; set; }

[BsonElement("coord")]
public float[] Coordinates { get; set; }
public double[] Coordinates { get; set; }
mayaraman19 marked this conversation as resolved.
Show resolved Hide resolved

public string Street { get; set; }

Expand Down Expand Up @@ -63,3 +63,56 @@ public class Review
}

// end-review-model

// start-ingredient-model

public class Ingredient
{
public int Id { get; set; }

public string Name { get; set; }

public int Price { get; set; }

public int Count { get; set; }
}

// end-ingredient-model

// start-bitAnd-example

var query = queryableCollection
.Where(i => i.Name == "eggs")
.Select(i => i.Price & i.Count);

// end-bitAnd-example

// start-bitAnd-collection-example

var query = queryableCollection
.Select(i => i.Price & i.Count);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an odd example. There's no real reason to bit-and a Price and a Count.

A better example would involve flags or something.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the example!


// end-bitAnd-collection-example

// start-bitOr-example

var query = queryableCollection
.Where(i => i.Name == "eggs")
.Select(i => i.Price | i.Count);

// end-bitOr-example

// start-bitNot-example

var queryable = collection
.Select(i => ~i.Count);

// end-bitNot-example

// start-bitXor-example

var query = queryableCollection
.Where(i => i.Name == "eggs" || i.Name == "watermelon")
.Select(i => i.Price ^ i.Count);

// end-bitXor-example
Loading