Is there any easy way of firing Mongo query in Java??
db.Test.aggregate(
[
{
'$match':
{
'o': { '$gt': [] }
}
},
{
'$project': {
'uid': 1,
'o': 1
}
},
{
'$project': {
'_id': 0,
'uid': 1,
o: {
$filter: {
input: "$o",
as: "item",
cond: {
$and: [
{
$lt: [ "$$item.ad", 0 ]
},
{
$lt: [ "$$item.at", 0 ]
}
]
}
}
}
}
},
{
'$match': {
'o': { '$gt': []}
}
},
{
$project: {
uid: 1,
"mids": "$o.mid"
}
},
{
$unwind: "$mids"
},
{
$group: {
_id: {
uid: "$uid",
mid: "$mids"
},
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
uid: "$_id.uid",
mid: "$_id.mid",
count: 1
}
}
]
);
Is http://jongo.org serve the purpose for complex queries?
As an alternative, you can use the Java driver's Document.parse() method. You can supply a JSON string to the method (following MongoDB's extended JSON formatting), and it will return a parsed BSON document for you.
Please see http://mongodb.github.io/mongo-java-driver/3.5/javadoc/org/bson/Document.html#parse-java.lang.String- for the method's documentation.
Related
I am trying to build a Mongo query for the below-mentioned record,
[
{
"_id": "1",
"source": "Source1",
"customer": "customer1",
"models": [
{ "modelid": "modelid123" },
{ "modelid": "modelid124" }
],
"accounts": [
{
"account": "acc1",
"models": [
{ "modelid": "modelid123" },
{ "modelid": "modelid124" }
],
"packages": [
{
"package": "p1",
"models": [
{ "modelid": "modelid123" },
{ "modelid": "modelid125" }
]
}
]
},
{
"account": "acc2",
"models": [
{ "modelid": "modelid123" },
{ "modelid": "modelid126" }
],
"packages": [
{
"package": "p2",
"models": [
{ "modelid": "modelid123" }
]
}
]
}
]
}
]
My expected o/p is
source customer account package
Source1 customer1
Source1 customer1 acc1
Source1 customer1 acc1 p1
Source1 customer1 acc2
Source1 customer1 acc2 p2
As modelid123 is present at all the above levels.
I tried using $unwind aggregate functions to achieve the above but I am getting duplicate rows.
db.collection.aggregate([
{ $unwind: { path: "$models", preserveNullAndEmptyArrays: true } },
{ $unwind: { path: "$accounts", preserveNullAndEmptyArrays: true } },
{ $unwind: { path: "$accounts.models", preserveNullAndEmptyArrays: true } },
{ $unwind: { path: "$accounts.packages.models", preserveNullAndEmptyArrays: true } },
{
$match: {
"$and": [ { "source": "Accurate" } ],
"$or": [
{ "models.modelId": { "$in": [ "model1234" ] } },
{ "accounts.models.modelId": { "$in": [ "model1234" ] } },
{ "accounts.packages.models.modelId": { "$in": [ "model1234" ] } }
]
}
}
])
`Problem here is that I can not restructure the data. I can do this on the code side using Java but I want to achieve pagination also. I am using Spring data mongodb to connect.
You can use $group to deal with duplicae. I think you can learn from my answer and correct your own one.
db.collection.aggregate([
{ $unwind: { path: "$models", preserveNullAndEmptyArrays: true } },
{ $unwind: { path: "$accounts", preserveNullAndEmptyArrays: true } },
{ $unwind: { path: "$accounts.models", preserveNullAndEmptyArrays: true } },
{ $unwind: { path: "$accounts.packages", preserveNullAndEmptyArrays: true } },
{ $unwind: { path: "$accounts.packages.models", preserveNullAndEmptyArrays: true } },
{
$project: {
_id: 0,
source: "$source",
customer: "$customer",
account: {
$cond: {
if: { $eq: [ "$models.modelid", "modelid123" ] },
then: "$accounts.account",
else: null
}
},
package: {
$cond: {
if: {
$and: [
{ $eq: [ "$models.modelid", "modelid123" ] },
{ $eq: [ "$accounts.packages.models.modelid", "modelid123" ] }
]
},
then: "$accounts.packages.package",
else: null
}
}
}
},
{
$group: {
_id: "$$ROOT",
count: { $sum: 1 }
}
},
{
$replaceWith: "$_id"
}
])
https://mongoplayground.net/p/u-cMvnuao1t
MongoDB Data:
{
"_id" : ObjectId("123"),
"attr" : [
{
"nameLable" : "First Name",
"userEnteredValue" : [
"Amanda"
],
"rowNumber":"1"
},
{
"nameLable" : "Last Name",
"userEnteredValue" : [
"Peter"
],
"rowNumber":"1"
},
{
"nameLable" : "First Name",
"userEnteredValue" : [
"Sandra"
],
"rowNumber":"2"
},
{
"nameLable" : "Last Name",
"userEnteredValue" : [
"Peter"
],
"rowNumber":"2"
}
]
}
Matching (First Name equals "Amanda" && Last Name equals "Peter") -> Match should happen within rowNumber so that i will get rowNumber1 record but now i am getting both rows as "Peter" happens to be in both "rowNumber" attribute.
Criteria Code:
Criteria cr = Criteria.where("attr").elemMatch(Criteria.where("nameLable").is(map.get("value1")).and("userEnteredValue").regex(map.get("value2").trim(), "i"); //Inside loop
AggregationOperation match = Aggregation.match(Criteria.where("testId").is("test").andOperator(cr.toArray(new Criteria[criteria.size()])));
DB Query for above search Criteria Match:
db.Col1.aggregate([
{
"$match":{
"testId":"test",
"$and":[
{
"attr":{
"$elemMatch":{
"nameLable":"First Name",
"userEnteredValue":{
"$regex":"Amanda",
"$options":"i"
}
}
}
},
{
"attr":{
"$elemMatch":{
"nameLable":"Last Name",
"userEnteredValue":{
"$regex":"Peter",
"$options":"i"
}
}
}
}
]
}
}
]
)
Please let me know how can we do match within "rowNumber" attribute.
Let me start by recommending you reconsider your document structure, I do not know your product but this structure is very unique and definitely makes most "simple" access patterns I can think of to very cumbersome to execute. This will be noticeable in my answer.
So the current query you have just required 2 separate elements in the array exist, as you mentioned you want the same rowNumber, due to the document structure this isn't really queryable, we will have to first use your query to match "potential" matching documents. At that point we can filter our the matched rows and see if we have both a first name and a last name matching.
Finally we could filter out the none matching rows from the result, here is the pipeline:
db.collection.aggregate([
{
"$match": {
"testId": "test",
"$and": [
{
"attr": {
"$elemMatch": {
"nameLable": "First Name",
"userEnteredValue": {
"$regex": "Amanda",
"$options": "i"
}
}
}
},
{
"attr": {
"$elemMatch": {
"nameLable": "Last Name",
"userEnteredValue": {
"$regex": "Peter",
"$options": "i"
}
}
}
}
]
}
},
{
$addFields: {
goodRows: {
"$setIntersection": [
{
$map: {
input: {
$filter: {
input: "$attr",
cond: {
$and: [
{
$eq: [
"$$this.nameLable",
"First Name"
]
},
{
"$regexMatch": {
"input": {
"$arrayElemAt": [
"$$this.userEnteredValue",
0
]
},
"regex": "Amanda",
"options": "i"
}
}
]
}
}
},
in: "$$this.rowNumber"
}
},
{
$map: {
input: {
$filter: {
input: "$attr",
cond: {
$and: [
{
$eq: [
"$$this.nameLable",
"Last Name"
]
},
{
"$regexMatch": {
"input": {
"$arrayElemAt": [
"$$this.userEnteredValue",
0
]
},
"regex": "Peter",
"options": "i"
}
}
]
}
}
},
in: "$$this.rowNumber"
}
}
]
}
}
},
{
$match: {
$expr: {
$gt: [
{
$size: "$goodRows"
},
0
]
}
}
},
{
$addFields: {
attr: {
$filter: {
input: "$attr",
cond: {
$in: [
"$$this.rowNumber",
"$goodRows"
]
}
}
}
}
}
])
Mongo Playground
I have data like this:
{ id : 1,
book: "Flash",
chapters: [
{
chap_no: "1",
sub_chapter: [
{sub_no: 1, description: "<description>"
},
{sub_no: 2, description: "<description>"
},
]
}
]
}
i want to show one field like this base on book -> chapter_no -> sub_no
{
sub_no: 2, description: "<description>"
}
in mongodb query.
$match
$unwind
$unwind
$match
$replaceRoot
db.collection.aggregate([
{
"$match": {
"chapters.sub_chapter.sub_no": 2
}
},
{
"$unwind": "$chapters"
},
{
"$unwind": "$chapters.sub_chapter"
},
{
"$match": {
"chapters.sub_chapter.sub_no": 2
}
},
{
"$replaceRoot": {
"newRoot": "$chapters.sub_chapter"
}
}
])
mongoplayground
you can make like this
db.collection.aggregate([
{
"$match": {
$and: [
{
"book": "Flash3"
},
{
"chapters.chap_no": "2"
},
{
"chapters.sub_chapter.sub_no": "1"
}
]
}
},
{
"$unwind": "$chapters"
},
{
"$unwind": "$chapters.sub_chapter"
},
{
"$match": {
$and: [
{
"book": "Flash3"
},
{
"chapters.chap_no": "2"
},
{
"chapters.sub_chapter.sub_no": "1"
}
]
}
},
{
"$replaceRoot": {
"newRoot": "$chapters.sub_chapter"
}
}
])
I want to get distinct categoryCode and categoryName while this document also contains list of accessories I want to count of this accessories in the response.
Sample data:
[
{
"categoryCode":"categoryCode1",
"categoryName":"categoryName1",
"accessories":[{"a_id":1},{"a_id":2}]
},
{
"categoryCode":"categoryCod2",
"categoryName":"categoryName2",
"accessories":[{"a_id":1},{"a_id":2},{"a_id":3}]
},
{
"categoryCode":"categoryCode1",
"categoryName":"categoryNam1",
"accessories":[{"a_id":1},{"a_id":2}]
}
]
Expected result:
[
{
"categoryCode":"categoryCode1",
"categoryName":"categoryName1",
"accessoriesCount":2
},
{
"categoryCode":"categoryCod2",
"categoryName":"categoryName2",
"accessoriesCount":3
}
]
https://mongoplayground.net/p/q6AZOaTwo5a
db.collection.aggregate([
{
"$group": {
"_id": {
categoryCode: "$categoryCode",
"categoryName": "$categoryName"
},
"accessories": {
"$addToSet": "$accessories"
}
}
},
{
"$project": {
categoryCode: "$_id.categoryCode",
categoryName: "$_id.categoryName",
accessoriesCount: {
$size: "$accessories"
},
_id: 0
}
}
])
Query
group to have the distinct values
$push the accesories arrays (we dont have $concat accumulator)
reduce those arrays to union them, keep only the distinct members, and take the count.
Test code here
db.collection.aggregate([
{
"$group": {
"_id": {
"categoryCode": "$categoryCode",
"categoryName": "$categoryName"
},
"accessories": {
"$push": "$accessories"
}
}
},
{
"$set": {
"accessoriesCount": {
"$size": {
"$reduce": {
"input": "$accessories",
"initialValue": [],
"in": {
"$setUnion": [
"$$value",
"$$this"
]
}
}
}
}
}
},
{
"$project": {
"_id": 0,
"categoryCode": "$_id.categoryCode",
"categoryName": "$_id.categoryName",
"accessoriesCount": "$accessoriesCount"
}
}
])
I have data in elasticsearch.
this is my actual doc https://docs.google.com/document/d/1DKID90I9ulUcut-S8UfrnSjY-3citEwmyfnJJmrIRU8/edit?usp=sharing
doc:
{
store_id:"abc",
event_timestamp:"2019-06-05 13:00:05",
event_type:"heartbeat"
}
I have store_id, range of dates and event type in the input.in output, I need the percentage amount of time device was online for that hour.
This is how we consider device online.
If there is an event="heartbeat" for a store_id in an hour then we say the store is online.
example 1.
so if the range is of "2019-05-07" to "2019-05-08" and there are 14 docs with different hour then the percentage will be (14/(2*24))*100
example 2.
doc:
{
store_id:"abc",
event_timestamp:"2019-06-05 13:00:05",
event_type:"heartbeat"
}
doc:
{
store_id:"abc",
event_timestamp:"2019-06-05 14:00:05",
event_type:"heartbeat"
}
doc:
{
store_id:"abc",
event_timestamp:"2019-06-05 14:00:05",
event_type:"heartbeat"
}
if input was store_id="abc" and date_range="2019-06-05" to ""2019-06-05" and event_type="heartbeat" then output would be (2/(1*24)) because there are only two different hour with event=heartbeat of that store.
this is my query for the cumulative sum.If some How I can divide the final cumulative sum with difference between dates.
GET /internship38/_search
{
"query":
{
"bool":
{
"must":
[
{
"match" :
{
"attributes.store_id" : "41b15888-0c2f-48f9-89d0-dc7aad19f52b"
}
},
{
"match":
{
"event_type":"app_sent_heartbeat"
}
}
]
}
},
"aggs":
{
"my_date_histo":{
"date_histogram":{
"field":"arrival_timestamp",
"interval":"day"
},
"aggs":
{
"distinct_hours": {
"cardinality": {
"script": {
"lang": "painless",
"source": "doc[params.date_field].value.hourOfDay;",
"params": {
"date_field": "arrival_timestamp"
}
}
}
},
"cumulative_hours": {
"cumulative_sum": {
"buckets_path": "distinct_hours"
}
}
}
}
}
}
Can It be done in java? for example https://www.programcreek.com/java-api-examples/?api=org.elasticsearch.script.Script
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-pipeline-bucket-script-aggregation.html
Above link in the elasticsearch documentation would help if you can reformat your query into "buckets" using the "aggs" functionality.
from link:
{
"size": 0,
"aggs" : {
"sales_per_month" : {
"date_histogram" : {
"field" : "date",
"calendar_interval" : "month"
},
"aggs": {
"total_sales": {
"sum": {
"field": "price"
}
},
"t-shirts": {
"filter": {
"term": {
"type": "t-shirt"
}
},
"aggs": {
"sales": {
"sum": {
"field": "price"
}
}
}
},
"t-shirt-percentage": {
"bucket_script": {
"buckets_path": {
"tShirtSales": "t-shirts>sales",
"totalSales": "total_sales"
},
"script": "params.tShirtSales / params.totalSales * 100"
}
}
}
}
}
}