mongo-pipebuilder 0.2.2__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mongo_pipebuilder/__init__.py +1 -1
- mongo_pipebuilder/builder.py +113 -20
- {mongo_pipebuilder-0.2.2.dist-info → mongo_pipebuilder-0.3.0.dist-info}/METADATA +235 -6
- mongo_pipebuilder-0.3.0.dist-info/RECORD +7 -0
- {mongo_pipebuilder-0.2.2.dist-info → mongo_pipebuilder-0.3.0.dist-info}/licenses/LICENSE +15 -0
- mongo_pipebuilder-0.2.2.dist-info/RECORD +0 -7
- {mongo_pipebuilder-0.2.2.dist-info → mongo_pipebuilder-0.3.0.dist-info}/WHEEL +0 -0
- {mongo_pipebuilder-0.2.2.dist-info → mongo_pipebuilder-0.3.0.dist-info}/top_level.txt +0 -0
mongo_pipebuilder/__init__.py
CHANGED
mongo_pipebuilder/builder.py
CHANGED
|
@@ -6,6 +6,8 @@ Builder Pattern implementation for safe construction of MongoDB aggregation pipe
|
|
|
6
6
|
|
|
7
7
|
Author: seligoroff
|
|
8
8
|
"""
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
9
11
|
from typing import Any, Dict, List, Optional, Union
|
|
10
12
|
|
|
11
13
|
# For compatibility with Python < 3.11
|
|
@@ -33,8 +35,7 @@ class PipelineBuilder:
|
|
|
33
35
|
Self for method chaining
|
|
34
36
|
|
|
35
37
|
Raises:
|
|
36
|
-
TypeError: If conditions is not a dictionary
|
|
37
|
-
ValueError: If conditions is None
|
|
38
|
+
TypeError: If conditions is None or not a dictionary
|
|
38
39
|
|
|
39
40
|
Example:
|
|
40
41
|
>>> builder.match({"status": "active", "age": {"$gte": 18}})
|
|
@@ -636,23 +637,15 @@ class PipelineBuilder:
|
|
|
636
637
|
"Only one output stage is allowed."
|
|
637
638
|
)
|
|
638
639
|
|
|
639
|
-
#
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
# If $merge exists, it must be the last stage
|
|
649
|
-
if has_merge:
|
|
650
|
-
merge_index = stage_types.index("$merge")
|
|
651
|
-
if merge_index != len(stage_types) - 1:
|
|
652
|
-
raise ValueError(
|
|
653
|
-
f"$merge stage must be the last stage in the pipeline. "
|
|
654
|
-
f"Found at position {merge_index + 1} of {len(stage_types)}."
|
|
655
|
-
)
|
|
640
|
+
# Check if $out or $merge exist and validate position
|
|
641
|
+
for stage_name in ["$out", "$merge"]:
|
|
642
|
+
if stage_name in stage_types:
|
|
643
|
+
stage_index = stage_types.index(stage_name)
|
|
644
|
+
if stage_index != len(stage_types) - 1:
|
|
645
|
+
raise ValueError(
|
|
646
|
+
f"{stage_name} stage must be the last stage in the pipeline. "
|
|
647
|
+
f"Found at position {stage_index + 1} of {len(stage_types)}."
|
|
648
|
+
)
|
|
656
649
|
|
|
657
650
|
return True
|
|
658
651
|
|
|
@@ -669,7 +662,7 @@ class PipelineBuilder:
|
|
|
669
662
|
>>> builder.get_stage_types()
|
|
670
663
|
['$match', '$limit']
|
|
671
664
|
"""
|
|
672
|
-
return [
|
|
665
|
+
return [next(iter(stage)) for stage in self._stages]
|
|
673
666
|
|
|
674
667
|
def has_stage(self, stage_type: str) -> bool:
|
|
675
668
|
"""
|
|
@@ -762,6 +755,106 @@ class PipelineBuilder:
|
|
|
762
755
|
self._stages.insert(position, stage)
|
|
763
756
|
return self
|
|
764
757
|
|
|
758
|
+
def get_stage_at(self, index: int) -> Dict[str, Any]:
|
|
759
|
+
"""
|
|
760
|
+
Get a specific stage from the pipeline by index.
|
|
761
|
+
|
|
762
|
+
Args:
|
|
763
|
+
index: Zero-based index of the stage to retrieve
|
|
764
|
+
|
|
765
|
+
Returns:
|
|
766
|
+
Dictionary representing the stage at the given index
|
|
767
|
+
|
|
768
|
+
Raises:
|
|
769
|
+
IndexError: If index is out of range
|
|
770
|
+
|
|
771
|
+
Example:
|
|
772
|
+
>>> builder = PipelineBuilder()
|
|
773
|
+
>>> builder.match({"status": "active"}).limit(10)
|
|
774
|
+
>>> stage = builder.get_stage_at(0)
|
|
775
|
+
>>> stage
|
|
776
|
+
{"$match": {"status": "active"}}
|
|
777
|
+
"""
|
|
778
|
+
if index < 0 or index >= len(self._stages):
|
|
779
|
+
raise IndexError(
|
|
780
|
+
f"Index {index} out of range [0, {len(self._stages)}]"
|
|
781
|
+
)
|
|
782
|
+
return self._stages[index].copy()
|
|
783
|
+
|
|
784
|
+
def pretty_print(self, indent: int = 2, ensure_ascii: bool = False) -> str:
|
|
785
|
+
"""
|
|
786
|
+
Return a formatted JSON string representation of the pipeline.
|
|
787
|
+
|
|
788
|
+
Useful for debugging and understanding pipeline structure.
|
|
789
|
+
|
|
790
|
+
Args:
|
|
791
|
+
indent: Number of spaces for indentation (default: 2)
|
|
792
|
+
ensure_ascii: If False, non-ASCII characters are output as-is (default: False)
|
|
793
|
+
|
|
794
|
+
Returns:
|
|
795
|
+
Formatted JSON string of the pipeline
|
|
796
|
+
|
|
797
|
+
Example:
|
|
798
|
+
>>> builder = PipelineBuilder()
|
|
799
|
+
>>> builder.match({"status": "active"}).limit(10)
|
|
800
|
+
>>> print(builder.pretty_print())
|
|
801
|
+
[
|
|
802
|
+
{
|
|
803
|
+
"$match": {
|
|
804
|
+
"status": "active"
|
|
805
|
+
}
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
"$limit": 10
|
|
809
|
+
}
|
|
810
|
+
]
|
|
811
|
+
"""
|
|
812
|
+
return json.dumps(self._stages, indent=indent, ensure_ascii=ensure_ascii)
|
|
813
|
+
|
|
814
|
+
def to_json_file(
|
|
815
|
+
self,
|
|
816
|
+
filepath: Union[str, Path],
|
|
817
|
+
indent: int = 2,
|
|
818
|
+
ensure_ascii: bool = False,
|
|
819
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
820
|
+
) -> None:
|
|
821
|
+
"""
|
|
822
|
+
Save the pipeline to a JSON file.
|
|
823
|
+
|
|
824
|
+
Useful for debugging, comparison with other pipelines, or versioning.
|
|
825
|
+
|
|
826
|
+
Args:
|
|
827
|
+
filepath: Path to the output JSON file (str or Path)
|
|
828
|
+
indent: Number of spaces for indentation (default: 2)
|
|
829
|
+
ensure_ascii: If False, non-ASCII characters are output as-is (default: False)
|
|
830
|
+
metadata: Optional metadata to include in the JSON file
|
|
831
|
+
|
|
832
|
+
Raises:
|
|
833
|
+
IOError: If file cannot be written
|
|
834
|
+
|
|
835
|
+
Example:
|
|
836
|
+
>>> builder = PipelineBuilder()
|
|
837
|
+
>>> builder.match({"status": "active"}).limit(10)
|
|
838
|
+
>>> builder.to_json_file("debug_pipeline.json")
|
|
839
|
+
|
|
840
|
+
>>> # With metadata
|
|
841
|
+
>>> builder.to_json_file(
|
|
842
|
+
... "pipeline.json",
|
|
843
|
+
... metadata={"version": "1.0", "author": "developer"}
|
|
844
|
+
... )
|
|
845
|
+
"""
|
|
846
|
+
filepath = Path(filepath)
|
|
847
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
848
|
+
|
|
849
|
+
output: Dict[str, Any] = {
|
|
850
|
+
"pipeline": self._stages,
|
|
851
|
+
}
|
|
852
|
+
if metadata:
|
|
853
|
+
output["metadata"] = metadata
|
|
854
|
+
|
|
855
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
856
|
+
json.dump(output, f, indent=indent, ensure_ascii=ensure_ascii)
|
|
857
|
+
|
|
765
858
|
def build(self) -> List[Dict[str, Any]]:
|
|
766
859
|
"""
|
|
767
860
|
Return the completed pipeline.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mongo-pipebuilder
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Type-safe, fluent MongoDB aggregation pipeline builder
|
|
5
5
|
Author-email: seligoroff <seligoroff@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -28,6 +28,12 @@ Dynamic: license-file
|
|
|
28
28
|
|
|
29
29
|
# mongo-pipebuilder
|
|
30
30
|
|
|
31
|
+
[](https://badge.fury.io/py/mongo-pipebuilder)
|
|
32
|
+
[](https://www.python.org/downloads/)
|
|
33
|
+
[](https://opensource.org/licenses/MIT)
|
|
34
|
+
[](https://github.com/psf/black)
|
|
35
|
+
[](https://github.com/seligoroff/mongo-pipebuilder)
|
|
36
|
+
|
|
31
37
|
Type-safe, fluent MongoDB aggregation pipeline builder for Python.
|
|
32
38
|
|
|
33
39
|
## Overview
|
|
@@ -36,11 +42,11 @@ Type-safe, fluent MongoDB aggregation pipeline builder for Python.
|
|
|
36
42
|
|
|
37
43
|
## Features
|
|
38
44
|
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
45
|
+
- **Type-safe**: Full type hints support with IDE autocomplete
|
|
46
|
+
- **Fluent interface**: Chain methods for readable, maintainable code
|
|
47
|
+
- **Zero dependencies**: Pure Python, lightweight package
|
|
48
|
+
- **Extensible**: Easy to add custom stages via `add_stage()`
|
|
49
|
+
- **Well tested**: Comprehensive test suite with 96%+ coverage
|
|
44
50
|
|
|
45
51
|
## Installation
|
|
46
52
|
|
|
@@ -259,6 +265,22 @@ group_index = stage_types.index("$group")
|
|
|
259
265
|
builder.insert_at(group_index, {"$addFields": {"x": 1}})
|
|
260
266
|
```
|
|
261
267
|
|
|
268
|
+
##### `copy() -> PipelineBuilder`
|
|
269
|
+
|
|
270
|
+
Creates an independent copy of the builder with current stages. Useful for creating immutable variants and composing pipelines.
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
builder1 = PipelineBuilder().match({"status": "active"})
|
|
274
|
+
builder2 = builder1.copy()
|
|
275
|
+
builder2.limit(10)
|
|
276
|
+
|
|
277
|
+
# Original unchanged
|
|
278
|
+
assert len(builder1) == 1
|
|
279
|
+
assert len(builder2) == 2
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
See [Composing and Reusing Pipelines](#composing-and-reusing-pipelines) for practical examples.
|
|
283
|
+
|
|
262
284
|
##### `validate() -> bool`
|
|
263
285
|
|
|
264
286
|
Validates the pipeline before execution. Checks that:
|
|
@@ -275,6 +297,54 @@ builder.add_stage({"$out": "output"}).match({"status": "active"})
|
|
|
275
297
|
builder.validate() # Raises ValueError: $out stage must be the last stage
|
|
276
298
|
```
|
|
277
299
|
|
|
300
|
+
##### `get_stage_at(index: int) -> Dict[str, Any]`
|
|
301
|
+
|
|
302
|
+
Gets a specific stage from the pipeline by index. Returns a copy of the stage.
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
builder = PipelineBuilder()
|
|
306
|
+
builder.match({"status": "active"}).limit(10)
|
|
307
|
+
stage = builder.get_stage_at(0) # Returns {"$match": {"status": "active"}}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
##### `pretty_print(indent: int = 2, ensure_ascii: bool = False) -> str`
|
|
311
|
+
|
|
312
|
+
Returns a formatted JSON string representation of the pipeline. Useful for debugging.
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
builder = PipelineBuilder()
|
|
316
|
+
builder.match({"status": "active"}).limit(10)
|
|
317
|
+
print(builder.pretty_print())
|
|
318
|
+
# [
|
|
319
|
+
# {
|
|
320
|
+
# "$match": {
|
|
321
|
+
# "status": "active"
|
|
322
|
+
# }
|
|
323
|
+
# },
|
|
324
|
+
# {
|
|
325
|
+
# "$limit": 10
|
|
326
|
+
# }
|
|
327
|
+
# ]
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
##### `to_json_file(filepath: Union[str, Path], indent: int = 2, ensure_ascii: bool = False, metadata: Optional[Dict[str, Any]] = None) -> None`
|
|
331
|
+
|
|
332
|
+
Saves the pipeline to a JSON file. Useful for debugging, comparison, or versioning.
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
builder = PipelineBuilder()
|
|
336
|
+
builder.match({"status": "active"}).limit(10)
|
|
337
|
+
|
|
338
|
+
# Basic usage
|
|
339
|
+
builder.to_json_file("debug_pipeline.json")
|
|
340
|
+
|
|
341
|
+
# With metadata
|
|
342
|
+
builder.to_json_file(
|
|
343
|
+
"pipeline.json",
|
|
344
|
+
metadata={"version": "1.0", "author": "developer"}
|
|
345
|
+
)
|
|
346
|
+
```
|
|
347
|
+
|
|
278
348
|
##### `build() -> List[Dict[str, Any]]`
|
|
279
349
|
|
|
280
350
|
Returns the complete pipeline as a list of stage dictionaries.
|
|
@@ -340,6 +410,150 @@ pipeline = (
|
|
|
340
410
|
)
|
|
341
411
|
```
|
|
342
412
|
|
|
413
|
+
### Composing and Reusing Pipelines
|
|
414
|
+
|
|
415
|
+
The `copy()` method allows you to create immutable variants of pipelines, enabling safe composition and reuse. This is useful when you need to:
|
|
416
|
+
- Create multiple variants from a base pipeline
|
|
417
|
+
- Compose pipelines functionally
|
|
418
|
+
- Cache base pipelines safely
|
|
419
|
+
- Pass pipelines to functions without side effects
|
|
420
|
+
|
|
421
|
+
#### Example: Building Multiple Variants from a Base Pipeline
|
|
422
|
+
|
|
423
|
+
```python
|
|
424
|
+
from mongo_pipebuilder import PipelineBuilder
|
|
425
|
+
|
|
426
|
+
# Base pipeline with common filtering and joining
|
|
427
|
+
base_pipeline = (
|
|
428
|
+
PipelineBuilder()
|
|
429
|
+
.match({"status": "published", "deleted": False})
|
|
430
|
+
.lookup(
|
|
431
|
+
from_collection="authors",
|
|
432
|
+
local_field="authorId",
|
|
433
|
+
foreign_field="_id",
|
|
434
|
+
as_field="author"
|
|
435
|
+
)
|
|
436
|
+
.unwind("author", preserve_null_and_empty_arrays=True)
|
|
437
|
+
.project({
|
|
438
|
+
"title": 1,
|
|
439
|
+
"authorName": "$author.name",
|
|
440
|
+
"publishedAt": 1
|
|
441
|
+
})
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
# Create variants with different sorting and limits
|
|
445
|
+
recent_posts = base_pipeline.copy().sort({"publishedAt": -1}).limit(10).build()
|
|
446
|
+
popular_posts = base_pipeline.copy().sort({"views": -1}).limit(5).build()
|
|
447
|
+
author_posts = base_pipeline.copy().match({"authorName": "John Doe"}).build()
|
|
448
|
+
|
|
449
|
+
# Base pipeline remains unchanged
|
|
450
|
+
assert len(base_pipeline) == 4 # Still has 4 stages
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
#### Example: Functional Composition Pattern
|
|
454
|
+
|
|
455
|
+
```python
|
|
456
|
+
def add_pagination(builder, page: int, page_size: int = 10):
|
|
457
|
+
"""Add pagination to a pipeline."""
|
|
458
|
+
return builder.copy().skip(page * page_size).limit(page_size)
|
|
459
|
+
|
|
460
|
+
def add_sorting(builder, sort_field: str, ascending: bool = True):
|
|
461
|
+
"""Add sorting to a pipeline."""
|
|
462
|
+
return builder.copy().sort({sort_field: 1 if ascending else -1})
|
|
463
|
+
|
|
464
|
+
# Compose pipelines functionally
|
|
465
|
+
base = PipelineBuilder().match({"status": "active"})
|
|
466
|
+
|
|
467
|
+
# Create different variants
|
|
468
|
+
page1 = add_pagination(add_sorting(base, "createdAt"), page=0)
|
|
469
|
+
page2 = add_pagination(add_sorting(base, "createdAt"), page=1)
|
|
470
|
+
sorted_by_name = add_sorting(base, "name", ascending=True)
|
|
471
|
+
|
|
472
|
+
# All variants are independent
|
|
473
|
+
assert len(base) == 1 # Base unchanged
|
|
474
|
+
assert len(page1) == 3 # match + sort + skip + limit
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
#### Example: Caching Base Pipelines
|
|
478
|
+
|
|
479
|
+
```python
|
|
480
|
+
from functools import lru_cache
|
|
481
|
+
|
|
482
|
+
@lru_cache(maxsize=100)
|
|
483
|
+
def get_base_pipeline(user_id: str):
|
|
484
|
+
"""Cache base pipeline for a user."""
|
|
485
|
+
return (
|
|
486
|
+
PipelineBuilder()
|
|
487
|
+
.match({"userId": user_id, "status": "active"})
|
|
488
|
+
.lookup(
|
|
489
|
+
from_collection="profiles",
|
|
490
|
+
local_field="userId",
|
|
491
|
+
foreign_field="_id",
|
|
492
|
+
as_field="profile"
|
|
493
|
+
)
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
# Reuse cached base pipeline with different modifications
|
|
497
|
+
user_id = "12345"
|
|
498
|
+
base = get_base_pipeline(user_id)
|
|
499
|
+
|
|
500
|
+
# Create multiple queries from cached base
|
|
501
|
+
recent = base.copy().sort({"createdAt": -1}).limit(10).build()
|
|
502
|
+
by_category = base.copy().match({"category": "tech"}).build()
|
|
503
|
+
with_stats = base.copy().group({"_id": "$category"}, {"count": {"$sum": 1}}).build()
|
|
504
|
+
|
|
505
|
+
# Base pipeline is safely cached and reused
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
#### Example: Pipeline Factories
|
|
509
|
+
|
|
510
|
+
```python
|
|
511
|
+
class PipelineFactory:
|
|
512
|
+
"""Factory for creating common pipeline patterns."""
|
|
513
|
+
|
|
514
|
+
@staticmethod
|
|
515
|
+
def base_article_pipeline():
|
|
516
|
+
"""Base pipeline for articles."""
|
|
517
|
+
return (
|
|
518
|
+
PipelineBuilder()
|
|
519
|
+
.match({"status": "published"})
|
|
520
|
+
.lookup(
|
|
521
|
+
from_collection="authors",
|
|
522
|
+
local_field="authorId",
|
|
523
|
+
foreign_field="_id",
|
|
524
|
+
as_field="author"
|
|
525
|
+
)
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
@staticmethod
|
|
529
|
+
def with_author_filter(builder, author_name: str):
|
|
530
|
+
"""Add author filter to pipeline."""
|
|
531
|
+
return builder.copy().match({"author.name": author_name})
|
|
532
|
+
|
|
533
|
+
@staticmethod
|
|
534
|
+
def with_date_range(builder, start_date: str, end_date: str):
|
|
535
|
+
"""Add date range filter to pipeline."""
|
|
536
|
+
return builder.copy().match({
|
|
537
|
+
"publishedAt": {"$gte": start_date, "$lte": end_date}
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
# Usage
|
|
541
|
+
base = PipelineFactory.base_article_pipeline()
|
|
542
|
+
johns_articles = PipelineFactory.with_author_filter(base, "John Doe")
|
|
543
|
+
recent_johns = PipelineFactory.with_date_range(
|
|
544
|
+
johns_articles,
|
|
545
|
+
start_date="2024-01-01",
|
|
546
|
+
end_date="2024-12-31"
|
|
547
|
+
).sort({"publishedAt": -1}).limit(10).build()
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
**Key Benefits:**
|
|
551
|
+
- Safe reuse: Base pipelines remain unchanged
|
|
552
|
+
- Functional composition: Build pipelines from smaller parts
|
|
553
|
+
- Caching friendly: Base pipelines can be safely cached
|
|
554
|
+
- No side effects: Functions can safely modify copies
|
|
555
|
+
- Thread-safe: Multiple threads can use copies independently
|
|
556
|
+
|
|
343
557
|
## Development
|
|
344
558
|
|
|
345
559
|
### Project Structure
|
|
@@ -374,3 +588,18 @@ See [DEVELOPMENT.md](DEVELOPMENT.md) for development guidelines.
|
|
|
374
588
|
MIT License - see [LICENSE](LICENSE) file for details.
|
|
375
589
|
|
|
376
590
|
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
mongo_pipebuilder/__init__.py,sha256=pP27GA8G6dttP-gMq9uNCJoS66-cb3JJiVVdI340er4,336
|
|
2
|
+
mongo_pipebuilder/builder.py,sha256=oQxRYL9ycjYCv2ErP_YHz-Uoo2pPRaRBaaaCLEsL5Mo,30286
|
|
3
|
+
mongo_pipebuilder-0.3.0.dist-info/licenses/LICENSE,sha256=-ZkZpDLHDQAc-YBIojJ6eDsMwxwx5pRuQz3RHnl9Y8w,1104
|
|
4
|
+
mongo_pipebuilder-0.3.0.dist-info/METADATA,sha256=KkgWrj5TD22yDj915Jrri_JftYMEpTz6hXSeHKEM7mk,15850
|
|
5
|
+
mongo_pipebuilder-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
+
mongo_pipebuilder-0.3.0.dist-info/top_level.txt,sha256=wLn7H_v-qaNIws5FeBbKPZBCmYFYgFEhPaLjoCWcisc,18
|
|
7
|
+
mongo_pipebuilder-0.3.0.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
mongo_pipebuilder/__init__.py,sha256=VF9G-Gp0kabZMoEmeQnSnSqHT3hLqxlEHY-FBeXfnVA,336
|
|
2
|
-
mongo_pipebuilder/builder.py,sha256=qvyQd1k9YbaIV0tRixQnsNr7yuE-_SSGN5tBSwDAk5E,27199
|
|
3
|
-
mongo_pipebuilder-0.2.2.dist-info/licenses/LICENSE,sha256=xAHmf48PmIziXYIdaJzRYeYpXFUPIb70SsSPhAHdggY,1089
|
|
4
|
-
mongo_pipebuilder-0.2.2.dist-info/METADATA,sha256=QZJAl4akVEiT9ucIaviwAwJuvTWFL9XyBEyKb_LI6jc,9127
|
|
5
|
-
mongo_pipebuilder-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
-
mongo_pipebuilder-0.2.2.dist-info/top_level.txt,sha256=wLn7H_v-qaNIws5FeBbKPZBCmYFYgFEhPaLjoCWcisc,18
|
|
7
|
-
mongo_pipebuilder-0.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|