How to best retrieve specific sub-elements of a document with a regex (or similar in mongo) - mongodb-query

I have a document with the following structure, is it possible (e.g. using $regex) to return just the position elements which start "^raw_" (as there can be many variants of raw_ elements)?
I can easily return all "positions" elements with a simple projection but can't seem to figure out (looking at examples in the doc's) if/how I can pull out just those that start with raw_ (I have tried a few variations of $elemMatch but without much luck).
Is it possible somehow? Can someone provide an example if so?
{
"_id": '1',
"positions": {
"raw_1": {
"123": 125.30,
"456": 125.30,
"789": 125.30,
},
"raw_2": {
"123": 125.30,
"456": 125.30,
"789": 125.30,
},
"calc_1": {
"123": 125.30,
"456": 125.30,
"789": 125.30,
},
"calc_2": {
"123": 125.30,
"456": 125.30,
"789": 125.30,
}
}
}

I don't think that it' possible to query against the document keys with $regex. If it's possible to adjust the document structure, I suggest the following:
{
"_id": "1",
"positions": {
"raw": [
{
"entry": "1",
"values": {
"123": 125.30,
"456": 125.30,
"789": 125.30,
}
},
{
"entry": "2",
"values": {
"123": 125.30,
"456": 125.30,
"789": 125.30,
}
}
],
"calc": [
{
"entry": "1",
"values": {
"123": 125.30,
"456": 125.30,
"789": 125.30,
}
},
{
"entry": "2",
"values": {
"123": 125.30,
"456": 125.30,
"789": 125.30,
}
}
]
}
}
Given this structure, it is much easier to return only the raw entries you are interested in:
db.col1.find({ "_id" : "1" },{ "positions.raw" : true });

Related

How to group by in MongoDB Compass based on the time?

Recently I'm working with a Mongodb database. Here is the data model of the document I need to run a query on:
{
"creation_date": {
"$date": {
"$numberLong": "1641981205813"
}
},
"user_id": {
"$oid": "61dedd8b7a520461dd78016b"
},
"products": [
{
"_id": {
"$oid": "61dede397a520461dd7818bd"
},
"product_id": {
"$oid": "615071ae8b66e1e9a3d6ea50"
},
"payment": true,
"support_all_payment": false,
"term_ids": null
}
],
"carts_info": [
{
"_id": {
"$oid": "61dede397a520461dd7818be"
},
"support_type": null,
"support_price": 0,
"product_price": 11000,
"product_type": "all",
"final_price": 11000,
"product_id": {
"$oid": "615071ae8b66e1e9a3d6ea50"
}
}
],
"_des": "initial_payment",
"_type": "online",
"_token": "9e0cb4d111f642f1a6f482bb04f1f57b",
"_price": 11000,
"_status": "unpaid",
"_terminal_id": "12605682",
"__v": 0,
"additional_information": {
"saman_bank": {
"MID": "0",
"ResNum": "61dede387a520461dd7818bb",
"State": "CanceledByUser",
"TraceNo": "",
"Wage": "",
"Rrn": "",
"SecurePan": "",
"HashedCardNumber": "",
"Status": "1"
}
}
}
This collection is user orders. I need to count the orders for today. So, I need such a equivalent query for Mongodb Compass the same as this SQL:
SELECT count(1) num,
date(creation_date) date
FROM orders
WHERE date(creation_date) = "2023-02-16"
GROUP BY date
Any idea how can I run this logic on Mongodb Compass?
Use $dateTrunc to perform date only operations.
db.collection.aggregate([
{
"$match": {
$expr: {
$eq: [
{
$dateTrunc: {
date: "$creation_date",
unit: "day"
}
},
ISODate("2022-01-12")
]
}
}
},
{
$group: {
_id: {
$dateTrunc: {
date: "$creation_date",
unit: "day"
}
},
num: {
$sum: 1
}
}
}
])
Mongo Playground
For OP's MongoDB v3.6, we can use $dateToString to perform string comparison on a date-only string.
db.collection.aggregate([
{
$addFields: {
dateOnly: {
"$dateToString": {
"date": "$creation_date",
"format": "%Y-%m-%d"
}
}
}
},
{
$match: {
dateOnly: "2022-01-12"
}
},
{
$group: {
_id: null,
num: {
$sum: 1
}
}
}
])
Mongo Playground

MongoDB get fieldvalue based on fieldname

I'm new to MongoDB, and I'm not sure if what I'm trying to do is possible at all. I've exhausted my Google skills, so I'm hoping someone here can give me a push in the right direction.
My data is structured like this:
db={
"people": [
{
"id": 1,
"name": "Pete",
"occupation": "baker"
},
{
"id": 2,
"name": "Mike",
"occupation": "painter"
}
],
"activity": [
{
"baker": "bakes",
"painter": "paints"
}
]
}
Although this is just sample data, my "activity" is a single large document with unique keys that I'm trying to get the value from, based on key name (when it matches value from the "people" document).
What I'm trying to achieve is this output:
[
{
"id": 1,
"name": "Pete",
"occupation": "baker”,
“activity:” “bakes”
},
{
"id": 2,
"name": "Mike",
"occupation": "painter”,
“activity”: “paints”
}
]
Is this possible at all?
If I understand your database and data model, it seems like a poor model.
If the data model was different (see below), the query would be quite simple. Given that, here's one way you could generate your desired output with your model.
db.people.aggregate([
{ "$lookup": {
"from": "activity",
"let": { occ: "$occupation" },
"pipeline": [
{
"$replaceWith": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"as": "kv",
"cond": { "$eq": [ "$$kv.k", "$$occ" ] }
}
}
}
}
],
"as": "activity"
}
},
{ "$set": {
"activity": {
"$getField": {
"field": "v",
"input": {
"$first": {
"$objectToArray": { "$first": "$activity" }
}
}
}
}
}
},
{ "$unset": "_id" }
])
Try it on mongoplayground.net.
If your activity collection has this model...
[
{
"occupation": "baker",
"activity": "bakes"
},
{
"occupation": "painter",
"activity": "paints"
}
]
... then the query could be:
db.people.aggregate([
{ "$lookup": {
"from": "activity",
"localField": "occupation",
"foreignField": "occupation",
"as": "activity"
}
},
{ "$set": { "activity": { "$first": "$activity.activity" } } },
{ "$unset": "_id" }
])
Try it on mongoplayground.net.

MongoDB Lookup values based on dynamic field name

I'm pretty sure the below can be done, I'm struggling to understand how to do it in MongoDB.
My data is structured like this (demo data):
db={
"recipes": [
{
"id": 1,
"name": "flatbread pizza",
"ingredients": {
"1010": 1,
"1020": 2,
"1030": 200
}
},
{
"id": 2,
"name": "cheese sandwich",
"ingredients": {
"1040": 1,
"1050": 2
}
}
],
"ingredients": [
{
"id": 1010,
"name": "flatbread",
"unit": "pieces"
},
{
"id": 1020,
"name": "garlic",
"unit": "clove"
},
{
"id": 1030,
"name": "tomato sauce",
"unit": "ml"
},
{
"id": 1040,
"name": "bread",
"unit": "slices"
},
{
"id": 1050,
"name": "cheese",
"unit": "slices"
}
]
}
The output I'm trying to achieve would look like this:
[
{
"id": 1,
"name": "flatbread pizza",
“flatbread”: “1 pieces”,
“garlic”: “2 cloves”,
“tomato sauce”: “200 ml”
},
{
"id": 2,
"name": "cheese sandwich",
“bread”: “1 slices”,
“cheese”: “2 slices”
}
]
I've tried several approaches, and I get stuck at the bit where I need to do a lookup based on the ingredient name (which actually is the id). I tried using $objectToArray to turn it into a k-v document, but then I get stuck in how to construct the lookup pipeline.
This is not a simple solution, and probably can be improved:
db.recipes.aggregate([
{
"$addFields": {
ingredientsParts: {
"$objectToArray": "$ingredients"
}
}
},
{
$unwind: "$ingredientsParts"
},
{
"$group": {
_id: "$id",
name: {
$first: "$name"
},
ingredientsParts: {
$push: {
v: "$ingredientsParts.v",
id: {
$toInt: "$ingredientsParts.k"
}
}
}
}
},
{
"$lookup": {
"from": "ingredients",
"localField": "ingredientsParts.id",
"foreignField": "id",
"as": "ingredients"
}
},
{
$unwind: "$ingredients"
},
{
"$addFields": {
"ingredientsPart": {
"$filter": {
input: "$ingredientsParts",
as: "item",
cond: {
$eq: [
"$$item.id",
"$ingredients.id"
]
}
}
}
}
},
{
$project: {
ingredients: 1,
ingredientsPart: {
"$arrayElemAt": [
"$ingredientsPart",
0
]
},
name: 1
}
},
{
"$addFields": {
units: {
k: "$ingredients.name",
v: {
"$concat": [
{
$toString: "$ingredientsPart.v"
},
" ",
"$ingredients.unit"
]
}
}
}
},
{
$group: {
_id: "$_id",
name: {
$first: "$name"
},
units: {
$push: "$units"
}
}
},
{
"$addFields": {
"data": {
"$arrayToObject": "$units"
}
}
},
{
"$addFields": {
"data.id": "$_id",
"data.name": "$name"
}
},
{
"$replaceRoot": {
"newRoot": "$data"
}
}
])
You can see it works here
As rickhg12hs said, it can be modeled better.

Create a new Google Sheet with row or column groups

I'm trying to create a new spreadsheet using spreadsheets#create, with specified row groups.
In the API Explorer, I am entering in the JSON below. which corresponds to the following appearance:
No errors are flagged or returned when I execute the call, but when the sheet is created, the specified grouping is not created - only the values are set.
{ "properties": {
"title": "Test Spreadsheet",
"locale": "en"
},
"sheets": [
{ "properties": {"title": "Test1"},
"data": [
{
"startRow": 0,
"startColumn": 0,
"rowData": [
{ "values": [
{ "userEnteredValue": { "stringValue": "Top1" } }
]
},
{ "values": [
{ "userEnteredValue": { "stringValue": "Top2" } }
]
},
{ "values": [
{ "userEnteredValue": { "stringValue": "" } },
{ "userEnteredValue": { "stringValue": "Top2A" } }
]
},
{ "values": [
{ "userEnteredValue": { "stringValue": "" } },
{ "userEnteredValue": { "stringValue": "Top2B" } }
]
},
{ "values": [
{ "userEnteredValue": { "stringValue": "" } },
{ "userEnteredValue": { "stringValue": "Top2C" } }
]
},
{ "values": [
{ "userEnteredValue": { "stringValue": "Top3" } }
]
}
]
}
],
"rowGroups": [
{ "range": {
"dimension": "ROWS",
"startIndex": 2,
"endIndex": 5
}
}
]
}
]
}
Even when I create the rowGroups JSON directly on the page, with its structured editor to make sure it is properly defined, the created spreadsheet still doesn't group the specified rows. I have triple-checked all my objects from the top down, and can't see what I am doing wrong.

Swagger Schema: oneOf, anyOf, allOf valid at the same time?

I'm just reading through the more advanced validators in the Schema definition of the Swagger specification:
{
"Schema":{
"type":"object",
"properties":{
"title":{
"type":"string"
},
"multipleOf":{
"type":"number",
"minimum":0,
"exclusiveMinimum":true
},
"maximum":{
"type":"number"
},
"exclusiveMaximum":{
"type":"boolean",
"default":false
},
"minimum":{
"type":"number"
},
"exclusiveMinimum":{
"type":"boolean",
"default":false
},
"maxLength":{
"type":"integer",
"minimum":0
},
"minLength":{
"type":"integer",
"minimum":0,
"default":0
},
"pattern":{
"type":"string",
"format":"regex"
},
"maxItems":{
"type":"integer",
"minimum":0
},
"minItems":{
"type":"integer",
"minimum":0,
"default":0
},
"uniqueItems":{
"type":"boolean",
"default":false
},
"maxProperties":{
"type":"integer",
"minimum":0
},
"minProperties":{
"type":"integer",
"minimum":0,
"default":0
},
"required":{
"type":"array",
"items":{
"type":"string"
},
"minItems":1,
"uniqueItems":true
},
"enum":{
"type":"array",
"items":{
},
"minItems":1,
"uniqueItems":true
},
"type":{
"type":"string",
"enum":[
"array",
"boolean",
"integer",
"number",
"object",
"string"
]
},
"not":{
"oneOf":[
{
"$ref":"#/definitions/Schema"
},
{
"$ref":"#/definitions/Reference"
}
]
},
"allOf":{
"type":"array",
"items":{
"oneOf":[
{
"$ref":"#/definitions/Schema"
},
{
"$ref":"#/definitions/Reference"
}
]
}
},
"oneOf":{
"type":"array",
"items":{
"oneOf":[
{
"$ref":"#/definitions/Schema"
},
{
"$ref":"#/definitions/Reference"
}
]
}
},
"anyOf":{
"type":"array",
"items":{
"oneOf":[
{
"$ref":"#/definitions/Schema"
},
{
"$ref":"#/definitions/Reference"
}
]
}
},
"items":{
"oneOf":[
{
"$ref":"#/definitions/Schema"
},
{
"$ref":"#/definitions/Reference"
}
]
},
"properties":{
"type":"object",
"additionalProperties":{
"oneOf":[
{
"$ref":"#/definitions/Schema"
},
{
"$ref":"#/definitions/Reference"
}
]
}
},
"additionalProperties":{
"oneOf":[
{
"$ref":"#/definitions/Schema"
},
{
"$ref":"#/definitions/Reference"
},
{
"type":"boolean"
}
],
"default":true
},
"description":{
"type":"string"
},
"format":{
"type":"string"
},
"default":{
},
"nullable":{
"type":"boolean",
"default":false
},
"discriminator":{
"$ref":"#/definitions/Discriminator"
},
"readOnly":{
"type":"boolean",
"default":false
},
"writeOnly":{
"type":"boolean",
"default":false
},
"example":{
},
"externalDocs":{
"$ref":"#/definitions/ExternalDocumentation"
},
"deprecated":{
"type":"boolean",
"default":false
},
"xml":{
"$ref":"#/definitions/XML"
}
},
"patternProperties":{
"^x-":{
}
},
"additionalProperties":false
}
}
The thing that I am thinking about is combinations of the anyOf, allOf, oneOf and not keywords. I have two questions.
The first question is: "can they be used in conjunction"? Like so:
{
"allOf" : [
{
"minItems" : 0
},
{
"maxItems" : 10
}
],
"anyOf" : [
{
"type" : "array",
"items" : {
"type" : "string"
}
},
{
"type" : "array",
"items" : {
"type" : "integer"
}
}
]
}
This example is, of course, needlessly complicated. But is it valid? Or can you only use one modifier but not the others?
The second question is, can anybody point me to a real-world example where one of these operators have been used in conjunction?
The thing that I am thinking about is combinations of the anyOf, allOf, oneOf and not keywords. I have two questions.
The first question is: "can they be used in conjunction"?
Yes, allOf, anyOf, oneOf and not can be used in conjunction. OpenAPI Specification follows the rules of JSON Schema here, and in JSON Schema adjacent keywords work as branches of an implicit allOf (source). So your example is equivalent to:
{
"allOf": [
{
"allOf": [
{
"minItems": 0
},
{
"maxItems": 10
}
]
},
{
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "array",
"items": {
"type": "integer"
}
}
]
}
]
}
That said, this example is too complex and can be simplified into:
{
"minItems": 0,
"maxItems": 10,
"type": "array",
"items": {
"oneOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
}
}