<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Betul Ince on Medium]]></title>
        <description><![CDATA[Stories by Betul Ince on Medium]]></description>
        <link>https://medium.com/@Betulince?source=rss-6cceeb5e0b89------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Fecf_VGE77nJJ9WHC__urg.png</url>
            <title>Stories by Betul Ince on Medium</title>
            <link>https://medium.com/@Betulince?source=rss-6cceeb5e0b89------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 10:11:43 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@Betulince/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Cyclomatic complexity is easy]]></title>
            <link>https://medium.com/@Betulince/cyclomatic-complexity-is-easy-193e2923f96e?source=rss-6cceeb5e0b89------2</link>
            <guid isPermaLink="false">https://medium.com/p/193e2923f96e</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[claude]]></category>
            <category><![CDATA[cyclomatic-complexity]]></category>
            <dc:creator><![CDATA[Betul Ince]]></dc:creator>
            <pubDate>Fri, 06 Feb 2026 17:55:31 GMT</pubDate>
            <atom:updated>2026-02-09T12:31:00.259Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1YmJiaAsh0v8PhunwGPGmw.png" /></figure><p>I started hearing the term cyclomatic complexity more often recently, especially with AI tools writing more and more of the code around us. I came across a <a href="https://www.linkedin.com/posts/gunnar-morling_i-swear-claude-is-coding-as-if-it-never-activity-7418320482149490688-2IQW?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAADDoGFgBc0KkUVeGIjdO81OPfUIkdebJlfI">joke from Gunnar Morling</a> about Claude writing code like it doesn’t care much about cyclomatic complexity, and it made me realize something: AI might be good at producing code, but not always good at keeping complexity under control. And we probably need to pay attention to the signals that show when the code is getting harder to understand.</p><p>Cyclomatic complexity is one of those signals. It’s a simple metric that tries to capture how complicated a piece of code is by looking at how many different paths it can take. A higher score means more execution paths, which, as you can imagine, makes the code harder to test. A lower score means fewer paths, which results in better understandability, and makes testing and maintenance easier.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d359d8df6b7d5c252b39195d04e1fd88/href">https://medium.com/media/d359d8df6b7d5c252b39195d04e1fd88/href</a></iframe><p>If you want to get a bit more concrete, cyclomatic complexity comes from graph theory. The lines of code can be visualized as the following control flow graph:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VAlE2qkDbLvJDbaGhLt__A.png" /></figure><p>The cyclomatic complexity is calculated as E − N + 2, where E is the number of edges and N is the number of nodes in the control flow graph. So, for the piece of code I shared, the cyclomatic complexity is 10 (19 edges minus 11 nodes plus 2). For quick mental math, you can simply think of it as the number of decision points plus one (the +1 represents the base path, which exists even though there are no branches in the code).</p><p>Although high cyclomatic complexity doesn’t necessarily mean the code is bad, sometimes it’s just the right solution for the problem. I went looking for a really complex piece of code in some open-source projects and found a method with a complexity of 40 in Apache Solr. My first thought was, “this must be messy,” but when I actually looked at it, it was pretty clean, just a binary serializer routing for different data types. I honestly couldn’t improve it without making it slower or harder to read. Some complexity comes from the problem you’re solving (you can’t avoid it), and some comes from bad design choices (you definitely should avoid it). Knowing the difference is far more useful than just blindly following a “keep it under 10” rule.</p><p>So what can we, as software engineers, do to potentially reduce cyclomatic complexity? Essentially, every if-else, for, while, or switch statement introduces a new path in your code’s flow. You can reduce complexity by breaking large methods into smaller, focused functions, using polymorphism or strategy patterns instead of long conditional chains, and leveraging lookup tables or maps where appropriate. It’s also useful to know the cyclomatic complexity when planning tests, because it helps you estimate how many test cases are needed to cover all possible paths through a method. By understanding the complexity, you can prioritize testing the most critical or risky paths first, ensuring better reliability and maintainability, though I still sometimes overlook a few cases on the first pass.</p><p>But here’s where cyclomatic complexity falls short: it treats all decision points equally. A deeply nested if statement inside a loop inside another conditional is much harder to reason about than three sequential if statements, even though they might have the same cyclomatic complexity score. This is where cognitive complexity comes in, a metric that tries to capture the actual mental burden of understanding code. We’ve all seen a method that technically wasn’t huge, but still took way too long to understand.</p><p>Cognitive complexity cares more about nested code, because that’s usually where things start to get harder to follow. A flat switch statement with 10 cases? Not that hard. Three levels of nested conditionals? Much harder to keep in your head. This is why that Apache Solr method with complexity 40 felt readable, it was essentially a flat structure routing different types, not a nested maze of conditions. The cyclomatic number was high, but the cognitive load was manageable.</p><p>Remember that joke about Claude not caring about cyclomatic complexity? There’s some truth there. AI tools are great at generating working code quickly, but they tend to write long, procedural functions that handle everything in one place.</p><p>Let me know if you’ve run into tricky high-complexity methods and what your approach was, and I’ll see you on the next one!</p><h4>References</h4><p><a href="https://dzone.com/articles/what-exactly-is-mccabe-cyclomatic-complexity">https://dzone.com/articles/what-exactly-is-mccabe-cyclomatic-complexity</a></p><p><a href="https://seeinglogic.com/posts/cyclomatic-complexity-illustrated/">https://seeinglogic.com/posts/cyclomatic-complexity-illustrated/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=193e2923f96e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using my Garmin watch data to explore KEDA, Prometheus, and Helm]]></title>
            <link>https://medium.com/@Betulince/using-my-garmin-watch-data-to-explore-keda-prometheus-and-helm-b34a3f2cfac2?source=rss-6cceeb5e0b89------2</link>
            <guid isPermaLink="false">https://medium.com/p/b34a3f2cfac2</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[keda]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[prometheus]]></category>
            <category><![CDATA[micronaut]]></category>
            <dc:creator><![CDATA[Betul Ince]]></dc:creator>
            <pubDate>Sun, 03 Aug 2025 22:06:34 GMT</pubDate>
            <atom:updated>2025-08-03T22:44:34.176Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*USCTCVce2U-wq3t6J75Llg.png" /></figure><p>A recent task at work got me diving into Kubernetes, KEDA, Prometheus, and Helm, and it didn’t take long to realize I had a lot to learn about how these tools work together.</p><p>So, I built a simple app that takes daily activity data, saves it to a MySQL database, exposes a REST API, and generates summary reports. I deployed it on Kubernetes, used Helm to manage the deployment, Prometheus for monitoring, and KEDA for autoscaling based on metrics.</p><p>It turned out to be a fun project, and I learned a lot. In this post, I’ll walk you through how I built and deployed the app, and what I learned along the way.</p><h3>Data</h3><p>First, I had to access the data that had been recorded over the last couple of months, and came across this GitHub repository: <a href="https://github.com/tcgoetz/GarminDB/wiki/Usage">https://github.com/tcgoetz/GarminDB</a>/. This repository contains Python scripts for parsing health data and manipulating it within an SQLite database.</p><p>I only used JSON files containing my activity data for this sample project, but you could also use FIT files (think of FIT files as the raw, detailed logs that your Garmin watch or fitness device records during your workouts) or implement a streaming approach. To get started, follow these steps:</p><p>1. Clone the repository:</p><pre>git clone https://github.com/tcgoetz/GarminDB.git</pre><p>2. Install the required package:</p><pre>pip install garmindb</pre><p>3. Set up the configuration file by creating the config directory and file:</p><pre>mkdir -p /Users/inceb/.GarminDb &amp;&amp; touch &#39;~/.GarminDb/GarminConnectConfig.json&#39;</pre><p>Then edit this <a href="https://raw.githubusercontent.com/tcgoetz/GarminDB/master/garmindb/GarminConnectConfig.json.example">file</a> to include your Garmin Connect username, password, and the start date of the data you want to download and copy it inside the json that you created.</p><p>4. Run the following command to download your Garmin data and build your local database:</p><pre>garmindb_cli.py --all --download --import --analyze </pre><p>This will store everything under ~/HealthData.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/976/1*yA9sflsGwdJ_Jjh8Vskggg.png" /></figure><p>In the FitFiles directory, you’ll find both the original FIT files and their corresponding JSON versions.</p><h3>Application</h3><p>What I wanted this app to do:</p><ul><li>Use Micronaut framework for lightweight, fast startup and built-in support for dependency injection, HTTP routing, and metrics.</li><li>Upload and ingest activity data through the /upload endpoint using JSON files.</li><li>Persist the uploaded data into a database in a normalized format.</li><li>Generate simple summary reports over a selected date range, showing total steps, distance, and calories.</li><li>Collect Prometheus metrics such as the number of uploads, which are later used for monitoring and autoscaling with KEDA.</li></ul><p>You can find the full application code on my GitHub: <a href="https://github.com/betulince/garmin-scaler">https://github.com/betulince/garmin-scaler</a></p><p><strong>Exposing Metrics with Micronaut and Using Them with KEDA</strong></p><p>To enable autoscaling based on custom metrics, I needed to expose application metrics in a way Prometheus could scrape them. Micronaut makes this pretty straightforward.</p><p>I enabled Micronaut’s built-in metrics and Prometheus exporter by adding these configurations in application.yml:</p><pre>micronaut:<br>  application:<br>    name: activity-analyzer<br>  metrics:<br>    enabled: true<br>    export:<br>      prometheus:<br>        enabled: true<br>endpoints:<br>  prometheus:<br>    enabled: true<br>    sensitive: false</pre><p>With this setup, the app exposes a /prometheus endpoint that outputs metrics in the Prometheus format.</p><p>In the application code, I used <a href="https://micronaut-projects.github.io/micronaut-micrometer/latest/guide/">Micrometer</a> to create a gauge metric tracking the number of uploaded activities currently being processed. This metric is updated whenever activities are uploaded.</p><pre>@Singleton<br>public class ActivityMetrics {<br><br>    private final AtomicInteger uploadedActivitiesGauge = new AtomicInteger(0);<br><br>    public ActivityMetrics(MeterRegistry meterRegistry) {<br>        Gauge.builder(&quot;activities.uploaded.gauge&quot;, uploadedActivitiesGauge, AtomicInteger::get)<br>                .description(&quot;Current count of uploaded activities&quot;)<br>                .register(meterRegistry);<br>    }<br><br>    public void incrementGauge() {<br>        uploadedActivitiesGauge.incrementAndGet();<br>    }<br>    <br>}</pre><p>And in the controller method:</p><pre>@Post(value = &quot;/upload&quot;, consumes = MediaType.APPLICATION_JSON)<br>public HttpResponse&lt;Map&lt;String, Object&gt;&gt; uploadActivityJson(@Body DailyActivityDto activityDto) {<br>    try {<br>        DailyActivity saved = activityService.saveOrUpdateActivity(activityDto);<br>        activityMetrics.incrementGauge();<br>        return HttpResponse.ok(Map.of(<br>                &quot;status&quot;, &quot;success&quot;,<br>                &quot;activityId&quot;, saved.getActivityId()<br>        ));<br>    } catch (Exception e) {<br>        return HttpResponse.serverError(Map.of(<br>                &quot;error&quot;, e.getMessage()<br>        ));<br>    }<br><br>}</pre><p>KEDA connects to Prometheus, scrapes this metric from the /prometheus endpoint, and uses it to decide how many replicas of the app to run. When the number of uploaded activities exceeds a configured threshold, KEDA scales up the pods to handle the increased load. When the metric goes down, KEDA scales back down.</p><h3>Deployment</h3><p>1. I created a Dockerfile that builds the Java app into a container image, ensuring all dependencies and configuration are bundled for consistent deployment.</p><p>2. I wrote Kubernetes deployment and service YAML templates to define how the app should run and be exposed inside the cluster. Then, I wrapped these templates inside a Helm chart to manage configuration and versioning more easily.</p><p>The Helm chart includes two important files:</p><ul><li><strong>Chart.yaml:</strong> This file contains metadata about the chart such as its name, version, description, and dependencies.</li><li><strong>values.yaml:</strong> This file holds the default configuration values for the chart, like image repository, replica count, service port, and custom settings for Prometheus and KEDA.</li></ul><p>3. To monitor the application, I included Prometheus as a subchart dependency in the Helm chart and configured it to disable unnecessary components like node-exporter, and alertmanager for a leaner deployment.</p><p>4. I defined a KEDA ScaledObject resource that uses Prometheus metrics to scale the app pods dynamically based on the number of uploaded activities.</p><p>5. I used helm install command to deploy the entire stack.</p><ul><li>Deploy Prometheus on Kubernetes:</li></ul><pre>helm repo add prometheus-community https://prometheus-community.github.io/helm-charts<br>helm repo update<br>helm install prometheus prometheus-community/prometheus</pre><ul><li>Set up KEDA for autoscaling:</li></ul><pre>helm repo add kedacore https://kedacore.github.io/charts<br>helm repo update<br><br>kubectl create namespace keda<br>helm install keda kedacore/keda --namespace keda --version 2.17.2</pre><p>I deployed KEDA in its own namespace to isolate its components (webhook, operator, metrics server) from the app workload.</p><ul><li>Package and deploy the app using its Helm chart. Make sure to provide the path to the chart directory:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/558/1*DGAxHzubD6XAxMUy_1rYrg.png" /></figure><ul><li>Make sure that the backend is deployed to the cluster:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oYrR6AQR8idyJuXZk1loqg.png" /></figure><p>To see all pods in all namespaces across the Kubernetes cluster:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hOenCjUJR0xz0kVdMWbegQ.png" /></figure><p><strong>Customizing Prometheus Deployment</strong></p><p>By default, the Prometheus Helm chart deploys several additional components including Node Exporter, Kube State Metrics, Alertmanager, and Pushgateway.</p><p>If you want to disable these unnecessary components, you can add the following configuration under the prometheus: section in your values.yaml file:</p><pre>prometheus:<br>  alertmanager:<br>    enabled: false<br>  kube-state-metrics:<br>    enabled: false<br>  prometheus-node-exporter:<br>    enabled: false<br>  prometheus-pushgateway:<br>    enabled: false</pre><p>And to simplify deployment, you can include Prometheus as a dependency inside your own Helm chart rather than installing it separately.</p><pre>apiVersion: v2<br>name: garmin-scaler<br>description: A Micronaut Garmin activity scaler app<br>type: application<br>version: 0.1.0<br>appVersion: &quot;1.0.0&quot;<br>dependencies:<br>  - name: prometheus<br>    version: 27.29.0<br>    repository: https://prometheus-community.github.io/helm-charts</pre><p>After adding this, run:</p><pre>helm dependency update ./helm/garmin-scaler</pre><p>This downloads the Prometheus chart into the charts/ directory. Now, when you install your chart with:</p><pre>helm install garmin-scaler ./helm/garmin-scaler</pre><p>Helm will automatically install Prometheus as a subchart, applying the customized values (such as disabling the extra components). This allows us to manage both the app and Prometheus together with a single Helm command, without needing to install Prometheus separately.</p><p><strong>Trigger rollout updates when needed</strong></p><p>When I made changes to the deployment (e.g., updated image or config), I used kubectl rollout restart deployment &lt;name&gt; to restart the pods gracefully and apply the changes without tearing everything down.</p><h3>Monitoring</h3><p>Before anything else, I used the following command to make my backend accessible from localhost:8080:</p><pre>kubectl port-forward svc/garmin-scaler 8080:8080</pre><p>This lets me access the /upload endpoint to send activity data for testing.</p><p>After deploying everything cleanly, I checked the Horizontal Pod Autoscaler (HPA):</p><pre>kubectl get hpa -w</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8iLx_UiNao5fphXvbmtl9g.png" /></figure><p>Once I uploaded Garmin activity data, things started moving. The metric hit the target value (3), so KEDA spun up 1 replica. As more metrics were reported (because more activities were still being processed), the metric target increased. The current metric (6) is now twice the target (3), which indicates the load is growing, and HPA prepares to scale further.</p><p>And indeed, after a short delay, replica count increased to 2, matching the need.</p><p>KEDA works by observing custom/external metrics (in my case, a Prometheus gauge based on the number of unprocessed activities). The HPA doesn’t instantly spike to max replicas. Instead, it scales gradually based on the metric-to-target ratio.</p><h3>Conclusion</h3><p>Finally,</p><ul><li>Prometheus scrapes the app’s metrics, and KEDA uses them to trigger scaling events based on workload.</li><li>By uploading activity data, I was able to observe KEDA scale the app up and down in real time.</li><li>This project helped me understand how all these tools fit together to make autoscaling possible.</li></ul><p>The shenanigans will continue in the next one. 🧙‍♀️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b34a3f2cfac2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Serialize all the way down]]></title>
            <link>https://medium.com/@Betulince/serialize-all-the-way-down-3876d8a77e84?source=rss-6cceeb5e0b89------2</link>
            <guid isPermaLink="false">https://medium.com/p/3876d8a77e84</guid>
            <category><![CDATA[serialization]]></category>
            <category><![CDATA[in-memory]]></category>
            <category><![CDATA[deserialization]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[protobuf]]></category>
            <dc:creator><![CDATA[Betul Ince]]></dc:creator>
            <pubDate>Tue, 01 Jul 2025 15:55:45 GMT</pubDate>
            <atom:updated>2025-07-01T15:55:45.888Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1015/1*weZcXBCUZSrGkrKiQzDlkw.png" /></figure><p>Serialization and deserialization are concepts I used for years before I truly understood how they work under the hood. Eventually, I took some time to dig deeper and make sense of it all. This article won’t go too deep into the internals, but if you often use serialization and still find parts of it a bit unclear, we’ll go through a few key questions to help make it easier to understand.</p><ul><li>What is serialization, and what problem does it solve?</li><li>What are the common use cases?</li><li>How does it work under the hood?</li><li>How can you implement it safely and effectively?</li><li>And finally, what does it look like in a real-world example?</li></ul><h3>What is serialization, and what problem does it solve?</h3><p>Think of it like turning your favorite dish into a recipe.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CvUWRP6ixyu69tlijgVf_A.png" /></figure><ul><li>The pancake is the real, delicious object built from its ingredients (data).</li><li>The recipe is the serialized version: a transferrable format that someone else can read.</li><li>When another cook reads the recipe and follows the steps, they deserialize it into a new pancake (object) <em>that looks just like the original</em>.</li></ul><p>But why do we need this?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SBguNPI3Y7u3_InbXtg6Aw.png" /><figcaption>Serialization/Deserialization Diagram</figcaption></figure><p>When you’re working with data in a program, it lives in memory, inside data structures like lists, trees, hash maps, or one of many other data structures. These structures are built in a way that makes sense only inside the program that’s running. The object exists as bytes, and your program accesses those bytes through memory addresses (pointers 🤡).</p><p>Here’s the problem:</p><p>These pointers are only meaningful within the same process and machine. If you want to send this object over the network, save it to a file, or pass it between services or systems, those internal references won’t make sense outside your program. Basically, the object needs to be translated into a format that’s independent of its in-memory representation.</p><p>That’s where serialization comes in.</p><blockquote>Serialization is the process of converting an in-memory object into a byte sequence or structured format (like JSON, Protobuf, or Avro) that can be stored or transmitted. The reverse process is called deserialization.</blockquote><h3>What are the common use cases?</h3><p>Serialization is more than just converting objects into stream of bytes. It’s an essential part of your application especially when different components need to talk to each other, or store data, or exchange messages <em>reliably. </em>It’s basically everywhere.</p><p>Here are some common scenarios:</p><ul><li>When services talk to each other over HTTP or messaging systems, they send data in serialized formats like JSON or Protocol Buffers. For example, a REST API sends JSON responses created by converting internal objects.</li><li>Data stored in memory caches or saved to databases and files also needs to be serialized so it can be saved and loaded later. For example, Redis and Memcached store data in a serialized form to reduce the load on databases.</li><li>Events, which represent changes in state or commands, are sent as serialized messages through message brokers. For example, Kafka events are often serialized using Avro or JSON.</li><li>Remote Procedure Calls (RPC) send method inputs and outputs between machines by serializing them. For example, gRPC uses Protocol Buffers to serialize these messages.</li></ul><h3><strong>H</strong>ow does it work under the hood?</h3><h4><strong>Runtime vs compile-time serialization</strong></h4><ul><li><strong>Runtime serialization: </strong>This approach uses reflection (we’ll talk about reflection in another article), or metadata (data that describes other data) inspection at runtime to dynamically convert objects to and from a serialized form. For example, Java’s built-in serialization or Python’s pickle operate this way. They inspect the object’s structure <strong>on the fly</strong> and serialize or deserialize accordingly without requiring pre-generated code.</li></ul><blockquote>Metadata inspection means that the program at runtime looks at the metadata associated with an object to determine how to convert that object into a serialized form.</blockquote><ul><li><strong>Compile-time serialization:</strong> This method involves generating serialization and deserialization code <strong>ahead of time, </strong>usually during the build process. For example, in Protocol Buffers, the .proto definition file is compiled to generate code that handles serialization for the defined message classes.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*WKVmjrPryONOmwQ6X6Jh9w.png" /><figcaption><a href="https://protobuf.dev/overview/">https://protobuf.dev/overview/</a></figcaption></figure><h4><strong>Schema and compatibility</strong></h4><p>Schema-based formats define the structure of data explicitly, allowing forward and backward compatibility by tolerating missing or extra fields during deserialization. This schema acts as a contract between the producer and consumer of the data.</p><ul><li><strong>Forward and backward compatibility: </strong>One thing that confused me was: why does adding a field to a class break deserialization in some systems but not others?<br>When an object is serialized, its structure is captured, and expected to match what the receiving code knows during deserialization. So, if you add a new field to a class, and try to deserialize older data (which doesn’t have this field), you cause a schema mismatch.<br>Now the schema-based formats come into play. Schema-based formats are designed to handle schema evolution gracefully.<br>If your new code can read data written by an older version, your system is backward compatible. If the older code can read data written by the newer version, it’s forward compatible. For reliable systems, we usually want both.</li><li><strong>Tolerance to missing or extra fields:</strong> During deserialization, if a field is missing in the serialized data but expected by the schema, the deserializer can use a default value or ignore it, or if extra fields are present in the data but not recognized by the schema, they can be safely ignored without breaking the deserialization process.</li><li><strong>Efficiency and type safety:</strong> Because the schema is known ahead of time, schema-based formats like <a href="https://protobuf.dev/overview/">Protocol Buffers</a>, <a href="https://avro.apache.org/">Apache Avro</a>, and <a href="https://github.com/google/flatbuffers">FlatBuffers</a> can serialize data more compactly and efficiently than text-based formats, and provide type safety, ensuring that the data conforms to the expected types defined in the schema.</li></ul><h3>How can you implement it safely and effectively</h3><h4><strong>Available tools</strong></h4><p>While most programming languages offer built-in serialization tools (like Java’s Serializable or Python’s pickle), it’s usually not a good idea to rely on them, especially when working with data from untrusted sources. This is because these native serialization mechanisms can introduce serious security risks.</p><p>For example, in Java, when you deserialize an object using ObjectInputStream.readObject(), the system can create instances of <strong>any class</strong> on the classpath that implements Serializable. This process bypasses constructors, which normally run important setup and validation code.</p><p>But what exactly does that mean?</p><p>Skipping constructors means objects can be created in unexpected or unsafe states. Even worse, hackers can create fake serialized data that tricks your system into running harmful code. This opens up many security risks and is a common way attackers cause problems.</p><p>Because of these risks, it’s safer to use formats like JSON, Protobuf, or Avro. They store data in a clear, standard way that’s easier to check and harder to misuse. Plus, they work across different languages and make updating data easier without causing errors.</p><p>With native serialization, even small changes to your classes (like adding or removing fields) can cause deserialization to fail or behave incorrectly.</p><p>Here is a very basic implementation of JSON Serialization with Jackson:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a04a9b291b994b95837f059beca5f46f/href">https://medium.com/media/a04a9b291b994b95837f059beca5f46f/href</a></iframe><p>Serialize to JSON:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9a9a353a2c0b456d56e6120209af4762/href">https://medium.com/media/9a9a353a2c0b456d56e6120209af4762/href</a></iframe><p>Deserialize from JSON:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3b215e6de01684035f28779b7b151f63/href">https://medium.com/media/3b215e6de01684035f28779b7b151f63/href</a></iframe><h4><strong>Performance trade-offs between formats</strong></h4><p>I’m not going to compare the performance of different serialization formats here just yet, and here’s the reason:</p><p><strong><em>Performance really depends on what you’re doing</em></strong><em>.</em></p><p>Different formats perform better or worse depending on the size, shape, and usage of your data. For example, if your data is simple and flat, most formats will work just fine. But if your data is large or has a lot of nested structures (like lists inside objects), some formats like Protobuf or Avro will handle it much faster and with smaller size than something like JSON.</p><p>Performance also depends on the programming language you’re using and how often you’re reading or writing the data. Some formats work really fast in languages like Java, but not as well in others. Also, if you’re sending data over a network many times per second, speed really matters, and binary formats are better for that. But if you’re just saving data to a file now and then, formats that are easier to read (like JSON) might be totally fine.</p><p>Lastly, other things like compression, network speed, and how often the data is used can make a big difference.</p><p>There’s no one “best” format. It really depends on your specific situation, so the best thing you can do is try a few and see what works best for your case.</p><p>We will look at an example of this in the next chapter.</p><h3>And finally, what does it look like in a real-world example?</h3><p>To understand where serialization fits into the bigger picture, I’d like to consider a company like LinkedIn, which handles billions of requests daily, such as viewing a company page, checking network connections, or sending messages, across web and mobile platforms. If you’re a LinkedIn user, you’ve likely noticed how quickly these actions are processed, with minimal latency.</p><p>So how do they achieve that level of performance?</p><p>This brings us to serialization.</p><p>At LinkedIn, their internal framework, <a href="http://linkedin.github.io/rest.li/">Rest.li</a>, originally used JSON as the default serialization format. JSON is widely supported and human-readable, which makes debugging easier. But in high performance systems, these advantages are not enough.</p><p>JSON is a textual format, which makes it quite verbose and that means larger payload sizes, more network usage, and longer processing times. Even with compression tools like gzip, the compression and decompression steps add extra CPU overhead, especially in environments like LinkedIn’s where services are written in garbage-collected languages like Java. JSON also struggles with serialization and deserialization throughput, which becomes a serious bottleneck at LinkedIn’s scale.</p><p>To improve this, they needed something:</p><ul><li>Compact (to reduce payload size and network latency)</li><li>Fast (for both serialization and deserialization)</li><li>Widely supported across many programming languages</li><li>Easy to plug into their existing Rest.li infrastructure</li></ul><p>They explored several options but Protobuf (Protocol Buffers) came out as the best choice. Since Protobuf is binary-based, the serialized data is much smaller and faster to process than JSON.</p><p>In the end, the switch to Protobuf brought these benefits:</p><ul><li>Up to 60% latency reduction for large payloads</li><li>6.25% increase in average throughput per host for responses</li><li>No noticeable downsides when compared to JSON</li></ul><p>This example shows how improving serialization boosts performance, scalability, and user experience, especially in microservices. You can check out <a href="https://www.linkedin.com/blog/engineering/infrastructure/linkedin-integrates-protocol-buffers-with-rest-li-for-improved-m">this article</a> for implementation details.</p><h3>Conclusion</h3><p>We’ve explored the fundamentals of serialization, and I hope it gave you a clearer understanding of how it works and why it matters.</p><p>Thank you for sticking around this far, and I’ll see you in the next one. Cheers! 🧚‍♀️</p><h3>References</h3><ul><li><a href="https://www.linkedin.com/blog/engineering/infrastructure/linkedin-integrates-protocol-buffers-with-rest-li-for-improved-m">LinkedIn Integrates Protocol Buffers With Rest.li for Improved Microservices Performance</a></li><li><a href="https://protobuf.dev/overview/">Overview</a></li><li><a href="https://blogs.oracle.com/javamagazine/post/java-json-serialization-jackson">Efficient JSON serialization with Jackson and Java</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3876d8a77e84" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building and running Apache Kafka from source code]]></title>
            <link>https://medium.com/@Betulince/building-and-running-apache-kafka-from-source-code-1855a9051b71?source=rss-6cceeb5e0b89------2</link>
            <guid isPermaLink="false">https://medium.com/p/1855a9051b71</guid>
            <category><![CDATA[streaming]]></category>
            <category><![CDATA[apache-kafka]]></category>
            <category><![CDATA[event-driven-systems]]></category>
            <dc:creator><![CDATA[Betul Ince]]></dc:creator>
            <pubDate>Wed, 12 Jun 2024 12:28:26 GMT</pubDate>
            <atom:updated>2026-02-09T12:31:28.015Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*n8ihv2IATV-cvIlPzPaVAg.jpeg" /><figcaption><a href="https://www.getorchestra.io/guides/apache-kafka-architecture">https://www.getorchestra.io/guides/apache-kafka-architecture</a></figcaption></figure><p>This blog post is for you if you’re interested in setting up Kafka on your local system from its source code. Whether you’re considering contributing to Apache Kafka or simply want to understand how to build and run such a significant project locally, this guide will help you get started.</p><p>I ran into some issues while building it myself, so I’ve included solutions for common Gradle and dependency problems as well.</p><p>In the end, we’ll check if Kafka works by creating a topic and trying out sending and getting messages.</p><p>But before we get started, let’s briefly explore what Apache Kafka is.</p><blockquote>Apache Kafka is an open-source distributed event streaming platform that is used to build real-time streaming applications and data pipelines.</blockquote><p>When we say ‘distributed’, we mean that Kafka can manage a lot of data by sharing the work across many servers. If the amount of data or the need for faster processing grows, more servers can be added to the Kafka cluster. This way, Kafka can handle the extra load without slowing down. This distributed setup makes it highly available and reliable. It copies data to several servers, so if one server fails, the data can still be accessed from another server.</p><p>Kafka can be used as a messaging system to handle communication between different components of a distributed system. Imagine you have a big system made up of many different parts, like servers, databases, and applications, all spread out across different computers. These parts need to talk to each other to share information and work together smoothly. Now, think of Kafka as the messenger that helps these parts communicate.</p><h4>Steps to Set Up Apache Kafka on Your Local Machine</h4><ul><li>Install necessary software and tools:</li></ul><p><strong>1. JDK:</strong> Kafka is written in Java and Scala, so you’ll need the JDK installed. Kafka requires JDK 8 or later. You can download it from <a href="https://www.oracle.com/java/technologies/downloads/?er=221886">Oracle</a>.</p><p><strong>2. Gradle:</strong> Gradle is the build automation tool used by Kafka. You can download it from <a href="https://gradle.org/install/">Gradle’s website</a> or use a package manager like <a href="https://brew.sh/">Homebrew</a> on macOS:</p><pre>brew install gradle</pre><p><strong>3. Git: </strong>Git is used to clone the Kafka source code repository. You can download it from the <a href="https://git-scm.com/downloads">Git website</a>.</p><p><strong>4. Apache Zookeeper: </strong>Zookeeper is required by Kafka for coordination between nodes. Kafka includes a Zookeeper server, so you can use the bundled version for simplicity, but you can also download it separately from the <a href="https://zookeeper.apache.org/releases.html">Zookeeper website</a>.</p><ul><li>Create a copy of the <a href="https://github.com/apache/kafka">Apache Kafka</a> project by forking the repository.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/980/1*KNdn461RlBWzEnwHtvctig.png" /></figure><p>After this, the URL of the project will change to: https://github.com/&lt;YourUserName&gt;/kafka.git</p><ul><li>Clone your forked project in your local machine:</li></ul><pre>git clone https://github.com/&lt;YourUserName&gt;/kafka.git</pre><ul><li>In your git bash, change the path to point to your repository directory:</li></ul><pre>cd kafka</pre><ul><li>Build a jar and run it:</li></ul><pre>./gradlew jar</pre><p>After the build is successful, remember that we need to run three main components in the following order to get Kafka up and running: first, Zookeeper; second, the Kafka server; and lastly, either the standalone or distributed server.</p><ul><li>Start the Zookeeper service:</li></ul><pre>./bin/zookeeper-server-start.sh config/zookeeper.properties</pre><p>Hmm, but what is Zookeeper?</p><p>Zookeeper is like a manager for distributed systems (like Kafka). Let’s say you have a group of workers (servers) that need to work together on a big project. Zookeeper helps them stay organized and ensures they can communicate with each other efficiently.</p><p>But before successfully starting zookeeper service, I encountered two problems that needed to be addressed:</p><p><strong>1. Classpath is empty.</strong></p><pre>Classpath is empty. Please build the project first e.g. by running &#39;./gradlew jar -PscalaVersion=2.13.14&#39; from source code</pre><p>Well, the build is already successful, isn’t it?</p><p>You might face this problem because of different reasons. If you look around online, you’ll see that some people resolved the issue by trying different approaches. However, in my case, the issue was with the folder name. The folder structure where you put the Kafka project shouldn’t contain any spaces in their names. After renaming the directory from “/open source” to /open-source” I could start the Zookeeper service successfully.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/552/1*8iPDAGs6whXpeqmHEx98jw.png" /></figure><p>(Yeah quite foolish of me, I know)</p><p><strong>2. SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6T-KG4eIglb37uIaTPx8-Q.png" /></figure><p>The link provided states that:</p><blockquote>This message is a <strong>warning</strong> and not an error. It is reported when no SLF4J providers could be found on the class path. SLF4J requires a logging provider because it is a logging API and not an logging implementation. Placing one (and only one) of the many available providers such as <em>slf4j-nop.jar</em> <em>slf4j-simple.jar</em>, <em>slf4j-reload4j.jar</em>, <em>slf4j-jdk14.jar</em> or <em>logback-classic.jar</em> on the class path will solve the problem.</blockquote><blockquote>In the absence of a provider, SLF4J will default to a no-operation (NOP) logger provider.</blockquote><p>So, when I checked the “dependant-libs-2.13.14” folder within my generated build folder after the build process, I noticed that only the slf4j-api library was present, but not its implementation. Therefore, I downloaded the implementation (I chose <em>slf4j-simple.jar) </em>from <a href="https://repo1.maven.org/maven2/org/slf4j/">Maven Central</a> and placed it in the same folder as the API.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/177/1*s8uc81jGCeXJaNb162JcGw.png" /></figure><p>This resolved the issue. It’s important to note that the versions must match. At first, I didn’t realize that the versions needed to match, so it didn’t work.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SceS37yIFjq02BJCrNZmXQ.png" /></figure><p>Now the Zookeeper server is running on port 2182.</p><ul><li>Start the Kafka server (run it in a new terminal):</li></ul><pre>./bin/kafka-server-start.sh config/server.properties</pre><p>This step starts the Kafka server, which facilitates communication between producers (who send messages) and consumers (who receive messages). This server operates on port 9092.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q0MZapM_TTAAC4IGyP9YaQ.png" /></figure><p>Now the server is running on port 9092.</p><ul><li>Run Kafka standalone server:</li></ul><pre>./bin/connect-standalone.sh config/connect-standalone.properties</pre><p>This step involves starting a server that can operate either in standalone mode or as part of a distributed system.</p><p><strong>Standalone server: </strong>This means the server runs by itself on a single computer. It’s good for development, testing, or small-scale projects. The server handles everything on its own without talking to other servers.</p><p><strong>Distributed server:</strong> In distributed mode, the server is part of a larger cluster of servers that work together. This setup is used for big projects and large amounts of data. The servers share the work and help each other, making sure the system runs smoothly and can handle failures.</p><ul><li>Create a topic:</li></ul><pre>bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 3 --topic local.kafka.setup.tutorial</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*abEMxdhPhjL0-BlQLVugYw.png" /></figure><ul><li>Run the producer in the terminal:</li></ul><pre>bin/kafka-console-producer.sh --topic local.kafka.setup.tutorial --bootstrap-server localhost:9092</pre><p>This command starts the Kafka console producer, specifying the topic ‘local.kafka.setup.tutorial’ and the Kafka server’s address ‘localhost:9092’. This producer allows you to input messages.</p><ul><li>Run the consumer in the terminal:</li></ul><pre>bin/kafka-console-consumer.sh --topic  local.kafka.setup.tutorial --bootstrap-server localhost:9092</pre><p>This consumer listens for the messages on the specified topic.</p><p>Now when you write a message in the producer, you should notice it showing up in the consumer:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VFz3A4zLa8ai03l0aCCF1Q.png" /></figure><p>This demonstrates the real-time communication between the producer and the consumer, facilitated by Apache Kafka.</p><ul><li>Use CTRL+C to stop running the application.</li></ul><h4>Conclusion</h4><p>Whether you’re new to Kafka or want to explore it further, building it yourself is a great way to learn. Thanks for sticking until the end, and see you on the next one! 🌱</p><h4>References</h4><ul><li><a href="https://kafka.apache.org/">Apache Kafka</a></li><li><a href="https://manasvigupta.github.io/getting-started-with-contributing-to-apache-kafka-part1-build-and-run-kafka-from-source-code/">Getting started with contributing to Apache Kafka (Part 1): Build and run Kafka from source code</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1855a9051b71" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Solving the Mystery of ifconfig]]></title>
            <link>https://medium.com/@Betulince/solving-the-mystery-of-ifconfig-fae0739e19b9?source=rss-6cceeb5e0b89------2</link>
            <guid isPermaLink="false">https://medium.com/p/fae0739e19b9</guid>
            <category><![CDATA[linux]]></category>
            <category><![CDATA[ifconfig]]></category>
            <category><![CDATA[networking]]></category>
            <dc:creator><![CDATA[Betul Ince]]></dc:creator>
            <pubDate>Fri, 17 Jun 2022 17:44:42 GMT</pubDate>
            <atom:updated>2022-06-17T17:44:42.531Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/591/1*AKqAYlxWaBQAFhOCrG1ihA.png" /><figcaption><a href="https://xkcd.com/1785/">https://xkcd.com/1785/</a></figcaption></figure><p>Hi all,</p><p>At the outset, if you want to read the explanation of the banner you can check it out from <a href="https://www.explainxkcd.com/wiki/index.php/1785:_Wifi">here</a>. I bet you’ll associate yourself with the situation of houseguest who has high tech-savviness. Actually this is the reason why I wrote this blog post. It was a nice Friday morning and I was just trying to connect to my institution’s WiFi in the library to be able to study. I am using Fedora Linux 35 (Workstation Edition) as an OS and what I go through with Linux distros can sometimes be a real pain in the neck. And it was one of those times.</p><p>Joking apart, Fedora wasn’t the problem. The main problem was that I had no idea of some terms and I found it beneficial to learn and explain them here.</p><p>Let’s kick off with what you encounter when you type <strong>ifconfig</strong> on your terminal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/702/1*BZqh5TN772-4wnFXDPnY3g.png" /><figcaption>Output of ifconfig command</figcaption></figure><p>Here, br-918f101aafe9, docker0, enp3s0f1, lo, wlp2s0 are the names of the active interfaces on our system. Running ifconfig (interface configuration) command shows information about all network interfaces currently in operation. It allows you to configure your network interfaces.</p><p>Okay but what does <em>“network interface”</em> actually mean? It is a software interface to networking hardware. At this point, it can be distinguished into two main types, physical and virtual. While physical network interface stands for an actual networking hardware, virtual network interface doesn’t need to represent any physical device.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*G1HnVmj8rzflOeFVpIPi0w.jpeg" /><figcaption>Network Interface Card (NIC)</figcaption></figure><p>The physical network interface appears as <strong>eth0</strong> which represents Ethernet Network Card whilst we can recognize the virtual network interface when we see <strong>lo</strong> which represents Loopback. There are different kinds of virtual interfaces other than Loopback like bridges, VLANs, tunnel interfaces and so on. Note that loopback is a special network interface that the system uses to communicate with itself.</p><h4>enp3s0f1</h4><p>Now let’s go back to the output of ifconfig. As I said above, eth0 stands for ethernet network interface but why my ethernet interface is named enp3s0f1 instead of eth0? That’s because of a change in how now udevd (systemd-udevd listens to kernel uevents. For every event, it executes matching instructions specified in udev rules) assigns names to ethernet devices. Now our devices use the “Predictable Interface Names”, which are based on:</p><ul><li>firmware/bios-provided index numbers for on-board devices for example eno1</li><li>firmware-provided pci-express hotplug slot index number for example ens1</li><li>physical/geographical location of the hardware for example enp2s0</li><li>the interface’s MAC address for example enx78e7d1ea46da</li><li>classic, unpredictable kernel-native ethX naming for example eth0</li></ul><p>I explained this because my first trouble connecting to eduroam was when there was a command I had to type using the name of my ethernet card. And I was expecting to see something like eth0. Without further ado, eth0 or in my case enp3s0f1, is used for communication with other computers on the network and on the Internet.</p><h4>lo</h4><blockquote>There’s no place like ::1</blockquote><p>lo is a special <em>virtual</em> network interface called <em>loopback device.</em> Loopback address specifies the local computer. Your computer uses lo in some cases like:</p><ul><li>communicating with itself</li><li>diagnostics and troubleshooting</li><li>connecting to servers running on the local machine</li></ul><p>When a network interface is disconnected (for example, when an Ethernet port is unplugged or Wi-Fi is turned off or not associated with an access point) no communication on that interface is possible, not even communication between your computer and itself. The loopback interface doesn’t indicate any actual hardware, but it exists so applications running on your computer can always connect to servers on the same machine.</p><p>If you didn’t get the joke at the beginning, ::1 is IPv6 loopback address which relates directly to the localhost host name meaning <em>this</em> computer.</p><h4>wlp2s0</h4><p>The relationship between eth0 and enp3s0f1, also exists between wlan0 and wlp2s0. wlan0 is the name of the wireless network interface on the system. It refers to a wireless local area network WiFi card. The name is broken down into two sections. The first is WLAN and the second is 0. WLAN is a wireless LAN, and 0 represents your card number. Because of the new naming scheme, we can encounter different kind of namings like wlp2s0.</p><blockquote>Basically, the first two letter describe the type of interface. ‘wl’ for wlan, ‘en’ for ethernet. The following code is often a description of the physical placement of the device in your computer — p2 is likely <a href="https://en.wikipedia.org/wiki/Conventional_PCI">PCI</a> bus 2, and s0 is likely slot 0.</blockquote><p>We can reveal if an interface is virtual or physical by examining the result of <em>ls -la </em>command run under the /sys/class/net directory:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/924/1*SavAevhXU-L32h5_OxqLpw.png" /><figcaption>/sys/class/net shows actual devices</figcaption></figure><p>You can see that wlp2s0 is not a virtual interface.</p><p><strong>docker0<br></strong>docker0 is a virtual bridge interface created by <em>Docker</em>. This bridge creates a separate network for docker containers and allows them to communicate with each other. It randomly chooses an address and subnet from a private defined range. All the Docker containers are connected to this bridge and use the NAT rules created by docker to communicate with the outside world. Docker will be the topic of another article.</p><p><strong>br-918f101aafe9</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/654/1*eGrHQq2vQD4kNowRn0YsXg.png" /><figcaption>Bridge Networking Interface</figcaption></figure><p>As you can decipher from the first two letters, br-918f101aafe9 is a bridge networking interface.</p><p>A Linux bridge behaves like a network switch. It forwards packets between interfaces that are connected to it. It’s usually used for forwarding packets on routers, on gateways, or between VMs and network namespaces on a host as you can see from the picture above.</p><p>Bridging your network connection is a handy method for sharing your internet connection between two or more computers. It’s useful if you can’t buy a router with more than one ethernet port, or if you’re an university student in a dorm room with limited ethernet jacks and no router.</p><p>Now let’s dive deep into interface details.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/564/1*h5ZeYsWVAyGYbu7CAFho2g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/594/1*W_PLKbeJpT3gl9HJFp-FDw.png" /><figcaption>Take a deeper look into the interfaces</figcaption></figure><p>Here you can see,</p><p><strong>UP:</strong> flag causes the interface to be activated.</p><p><strong>BROADCAST:</strong> indicates that interface is configured to handle broadcast packets, which is required for obtaining IP address via DHCP.</p><p><strong>MULTICAST:</strong> sets the multicast flag on the interface. It indicates that this interface can handle multicasting.</p><p><strong>mtu: </strong>maximum transmission unit. IP datagrams larger than MTU bytes will be fragmented into multiple Ethernet frames.</p><p>As an example 70:4d:7b:45:fd:30 is hardware address of the ethernet interface (also known as MAC address).</p><p><strong>inet: </strong>is IPv4 address assigned to the interface.</p><p><strong>netmask: </strong>indicates the TCP/IP address where two devices can tell if they are on the same network.</p><p><strong>txqueuelen length:</strong> sets<strong> </strong>the length of the transmit queue of the device.</p><p><strong>RX bytes: </strong>is a total number of bytes received over interface.</p><p><strong>TX bytes: </strong>is a total number of bytes transmitted over interface.</p><p><strong>RX packets: </strong>is a total number of packets received.</p><p><strong>TX packets: </strong>is a total number of packets transmitted.</p><p>Besides all the advantages we gained from ifconfig, ip command substituted its place by dint of capability to do a wide range of tasks. For example:</p><ul><li>Displaying/modifying interface properties.</li><li>Adding/removing ARP Cache entries along creating new static ARP entry for a host.</li><li>Displaying MAC addresses associated with all the interfaces.</li><li>Displaying and modifying kernel routing tables.</li></ul><p>While ifconfig mostly displays or modifies the interfaces of a system.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/915/1*vpkLKnD7B1fbqXejLkxnyA.png" /><figcaption>Output of ip command</figcaption></figure><p>I hope this post will be a fresh start for my future posts on the topic of computer networks. As always, thank you for reading so far!</p><h4><strong>References</strong></h4><ul><li><a href="https://www.computerhope.com/unix/uifconfi.htm#:~:text=ifconfig%20stands%20for%20%22interface%20configuration,network%20interfaces%20currently%20in%20operation">Linux ifconfig command help and examples</a></li><li><a href="https://wiki.debian.org/BridgeNetworkConnections">BridgeNetworkConnections</a></li><li><a href="https://codewithyury.com/demystifying-ifconfig-and-network-interfaces-in-linux/">Demystifying ifconfig and network interfaces in Linux</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fae0739e19b9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Here again with another Apache Solr contribution]]></title>
            <link>https://medium.com/@Betulince/here-again-with-another-apache-solr-contribution-7a29508e8881?source=rss-6cceeb5e0b89------2</link>
            <guid isPermaLink="false">https://medium.com/p/7a29508e8881</guid>
            <category><![CDATA[apache]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[solr]]></category>
            <category><![CDATA[contribution]]></category>
            <dc:creator><![CDATA[Betul Ince]]></dc:creator>
            <pubDate>Sun, 24 Apr 2022 07:12:37 GMT</pubDate>
            <atom:updated>2026-02-09T12:33:19.710Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D54L4U7Z6Rp9VIf1z-syyg.png" /></figure><p>In this blog post, I will explain how I started to make new contributions after the article I published with the name of “<a href="https://medium.com/segmentify-tech/my-apache-solr-contribution-journey-from-ui-improvement-to-bug-solving-d3380733deee">My Apache Solr Contribution Journey from UI Improvement to Bug Solving</a>” 3 months ago. I will explain how I added the screen where you can add parameter sets to Solr on the Admin UI and the section where you can use these sets in your queries on the query screen.</p><p>In order not to get lost in this pretty long blog, where I will make a lot of explanations about the improvements, I can divide them into the following headings:</p><ul><li>Where on earth did this paramsets feature idea come from?</li><li>How did I start?</li><li>What did I do during the development process?</li><li>Final results</li></ul><p>I want this article to cover the resources I used and the path I followed in the whole process. I know that if you proceed with a proper plan, you will contribute a lot to both yourself and the project.</p><p>Here you can access:</p><ul><li>The opened issue in JIRA for the development:<br><a href="https://issues.apache.org/jira/browse/SOLR-15853">https://issues.apache.org/jira/browse/SOLR-15853</a></li><li>README.md file including the example shown in this article:<br><a href="https://github.com/apache/solr/tree/main/solr/example/films">https://github.com/apache/solr/tree/main/solr/example/films</a></li><li>Pull request:<br><a href="https://github.com/apache/solr/pull/594">https://github.com/apache/solr/pull/594</a></li><li>The Request Parameters API documentation:<br><a href="https://solr.apache.org/guide/8_11/request-parameters-api.html">https://solr.apache.org/guide/8_11/request-parameters-api.html</a></li></ul><h3>Where on earth did this paramsets feature idea come from?</h3><p>It all started with this message from one of the Solr contributors, <a href="https://www.linkedin.com/in/epugh/">Eric Pugh</a>, who did a review on my previous commits:</p><blockquote>“If you feel ambitious, I’d love to see a basic UI for editing your ParamSets added to the Admin.<br><a href="https://nightlies.apache.org/solr/draft-guides/solr-reference-guide-main/request-parameters-api.html#the-request-parameters-endpoint">https://nightlies.apache.org/solr/draft-guides/solr-reference-guide-main/request-parameters-api.html#the-request-parameters-endpoint</a> <br>I’d be super happy to review that PR.”</blockquote><p>I had never used parameter sets before, I had never even heard of it. After examining Solr&#39;s documentation, I discovered that it is a super-powerful feature. And of course, I wanted to contribute and told Eric about it. He started by opening an issue in JIRA and explained to me in general terms what the parameter set does.</p><p>Here is how we can describe it briefly:</p><blockquote>ParamSets are a great way of organizing your parameters into an algorithm and then referring to it by name. I’ve used it to drive A/B testing, or just to simplify my experimentation.</blockquote><h3><strong>How did I start?</strong></h3><p>If you check you will see that there are not many sources other than Solr&#39;s documentation and films example under the /apache/solr/tree/main/solr/example file. So firstly I started by reading the Request Parameters API documentation and trying the set, delete, and update commands on the collection I created.</p><p>Before moving on, let’s summarize what a paramset is and what the main thing is to do with this feature.</p><p>You can do everything you see below with paramsets:</p><ul><li>To avoid frequently editing your solrconfig.xml to update request parameters that change often.</li><li>To reuse parameters across various request handlers.</li><li>To mix and match parameter sets at request time.</li><li>To avoid a reload of your collection for small parameter changes.</li></ul><p>The request to set or update request parameters is sent as a set of maps with names.</p><pre>&quot;set&quot;:{<br>    &quot;myFacets&quot;:{<br>      &quot;facet&quot;:&quot;true&quot;,<br>      &quot;facet.limit&quot;:5<br>    }<br>}</pre><p>In the following example, we are setting 2 sets of parameters named ‘myFacets’ and ‘myQueries’ by using Postman.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1010/1*pWZo_YgvVm4vroAreyP-ug.png" /></figure><p>These objects can be directly used in a request or a request handler definition. When making a request, the useParams parameter applies the request parameters sent to the request. This is translated at request time to the actual parameters. For example:</p><pre>http://localhost:8983/solr/techproducts/select?useParams=myQueries</pre><blockquote>And I should indicate that it is possible to pass more than one parameter set in the same request. This information will be useful in the next sections. For example:</blockquote><pre><a href="http://localhost/solr/techproducts/select?useParams=myFacets,myQueries">http://localhost:8983/solr/techproducts/select?useParams=myFacets,myQueries</a></pre><p>So far we have seen what a parameter set is and how it is used. The lack of a UI in the admin tool makes it a very opaque feature to use. You have to use Postman or a tool like that to manage this. This is exactly how the paramsets feature that we will add to the Admin panel will work. So if I need to divide the feature into two parts:</p><ol><li>A screen will be added to the admin panel where paramsets can be added, updated, deleted and all paramsets belonging to this collection can be seen. (This was the desired feature at first, then my mentor <a href="https://www.linkedin.com/in/ahmetcankepenek/">Ahmet Can Kepenek</a> said it would be cool if we could just use these paramsets in the query, then I suggested this idea and it was approved, so the second item below was created.)</li><li>The section where these parameters can be listed and used while sending queries will be added to the query screen.</li></ol><p>After spending some time understanding how paramset works and brainstorming what I can add more, I decided that I could start by adding the “paramsets” section to the <a href="https://solr.apache.org/guide/8_11/collection-specific-tools.html">Collection-Specific Tools</a> on the Admin UI.</p><p>And now it looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/171/1*BEBAlXOeZ3IqFpDvywKj0A.png" /></figure><p>To be honest, I found and created the icon next to the name of “Paramsets” from the first site that came up, just so I could see something there and be happy, but then I liked it very much and I didn’t change it! 😸</p><h3><strong>What did I do during the development process?</strong></h3><p>I will not talk about the whole process and the technical details under this title because I have already opened my final PR by making many changes as a result of the first review and the feedback. If you want to view the commits, you can check the GitHub link I shared at the top.</p><p>If I need to give an example, in the PR I opened before the first review, the paramsets were displayed as a list on the query screen and only one paramset could be selected, but since more than one paramset could be selected for the query; I was asked to turn this list into a <strong><em>multiple-selection</em></strong> drop-down menu, which I provided later.</p><p>For the <strong>Paramsets</strong> screen, I created these three new files: paramsets.html, paramsets.css, and paramsets.js. In the index.html file I added HTML and CSS files and the “Paramsets” as a menu item:</p><ul><li>For paramsets.css file:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3925de5c58c01c4539b926c4746acb06/href">https://medium.com/media/3925de5c58c01c4539b926c4746acb06/href</a></iframe><ul><li>For paramsets.js file:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/996160b7e96d1c08bba664797935c0a0/href">https://medium.com/media/996160b7e96d1c08bba664797935c0a0/href</a></iframe><ul><li>Inside the core-menu section:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cdf7ab6f3e0846c1f04f033a79a23263/href">https://medium.com/media/cdf7ab6f3e0846c1f04f033a79a23263/href</a></iframe><ul><li>Inside the collection-menu section:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/88ff8a442884fb99ccb72b940c73e99d/href">https://medium.com/media/88ff8a442884fb99ccb72b940c73e99d/href</a></iframe><p>In paramsets.js file I wrote the controller like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1b55019373a99faaf46819831f9f25cc/href">https://medium.com/media/1b55019373a99faaf46819831f9f25cc/href</a></iframe><p>Then I added the following code snippet in app.js file:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4a2bb0134fa562ca55ac99223176d8a/href">https://medium.com/media/b4a2bb0134fa562ca55ac99223176d8a/href</a></iframe><p>In order to have a <em>get</em> and <em>submit</em> object that I can use wherever I want in the application, I wrote a factory method in the services.js file to stick with the rule of make a more robust application in parallel with the purpose of the factory method, and I defined the <em>get</em> and <em>submit</em> that I used in the paramsets.js file here:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b9c0f9bfc8ff01c885b265e255cd1785/href">https://medium.com/media/b9c0f9bfc8ff01c885b265e255cd1785/href</a></iframe><p>An example of how I used “<em>submit”</em> in paramsets.js:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f5e6abec97e01d80b4bff6c66d0a1f70/href">https://medium.com/media/f5e6abec97e01d80b4bff6c66d0a1f70/href</a></iframe><p>In general, these are the old-timer code snippets in the PR I opened. And the final screen looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/930/1*tU7Wh1Lu1CpqssZ_uo3XJA.png" /></figure><p>With the provided convenience, if you want to see a sample json for the set command and copy-paste it to modify&amp;save you need to click the “Show sample json!” button, and then you will see the following on the screen:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/540/1*0p5OpVXrHMcMBXjz2XMGCA.png" /></figure><p>Let’s originate from <a href="https://github.com/apache/solr/tree/main/solr/example/films">the films example</a> and add algo_a and algo_b as parameter sets. From this point, I suggest you read the films example because I’ll not mention all the detail here. At the end of this blog post, you will witness the use of this example on the Admin UI with the addition of the paramsets feature.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/540/1*SJcma47DRW_r0oB4WzJlGQ.png" /></figure><p>After clicking <em>Submit Paramset</em> button you can see that our parameter sets are added in params.json successfully.</p><p>⚠️ Th<em>e parameters are stored in a file named </em><em>params.json.</em></p><p>On the right side of the screen, you can see the added paramsets:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/594/1*jRnEVygx_cvGV9E3lup10Q.png" /></figure><p>This was the screen where we played with paramsets. Now let’s move on to the section where we will use these paramsets, namely the query screen.</p><p>On this screen, you can see all the parameter sets you added on the Paramsets screen in the drop-down menu due to the changes made in the query.js file.</p><p>First of all, I used the controllerAs syntax in the query controller:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/860aaca7052d4bebd24708a3a78490ac/href">https://medium.com/media/860aaca7052d4bebd24708a3a78490ac/href</a></iframe><p>To provide multiple choices of the paramsets in the UI, I used a ui-select drop-down that supports search, filter, and multiple select features:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bc8392ac3cbe7c59eb6beb5fcfa42464/href">https://medium.com/media/bc8392ac3cbe7c59eb6beb5fcfa42464/href</a></iframe><p>In this section you can search for your paramsets, you can choose them or delete them by clicking the cross given next to your chosen param which corresponds to the delete action:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/269/1*kelEHdLXdSh0QRjN6h_GaA.png" /></figure><p>In the code snippet below, I provided the join action for multi-chosen paramsets.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4da6cf624b0f5bbfded60d2b413b198/href">https://medium.com/media/b4da6cf624b0f5bbfded60d2b413b198/href</a></iframe><p>After clicking the <em>Execute Query</em> button, you can see the successfully assigned paramsets to the <strong>useParams</strong> parameter in the URL.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/729/1*eHFkjcMz6Qz202T-NbOZwQ.png" /></figure><h3>Final Results</h3><p>As a result, you can see that we reached the final result of the films example. If I have to list the things to do in order with the parts that are not the subject of this blog:</p><ol><li>Create a new collection. Add field types and fields. Then feed the collection with the data given from the films sample.</li><li>Add the algo_a and algo_b parameter sets with the special query parameters they contain using the <em>set</em> command.</li><li>Use these parameters in queries one by one, and then multiple choice.</li><li>See that it works correctly!</li></ol><p>If you look at the screenshot below, you can see that we can use the paramsets as we want and it gives us the same results as in the example of the films.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/994/1*yhifL4_8JYRj7wHiKZ551w.png" /></figure><p>Before I finish my article, I would like to thank my mentor <a href="https://www.linkedin.com/in/ahmetcankepenek/">Ahmet Can Kepenek</a>, who supported me and helped me at every stage, and whom I could always ask my questions without hesitation. And I would like to thank <a href="https://www.linkedin.com/in/epugh/">Eric Pugh</a>, who never hesitated to give me credit during the review and gave me ideas for this new feature.</p><p>I look forward to seeing you in the next contribution story! 🚀</p><h3><strong>References</strong></h3><ul><li><a href="https://solr.apache.org/guide/8_8/request-parameters-api.html">Request Parameters API</a></li><li><a href="https://github.com/apache/solr/tree/main/solr/example/films">solr/solr/example/films at main · apache/solr</a></li><li><a href="https://github.com/querqy/chorus/blob/main/katas/007_organize_algorithms_using_paramsets.md">chorus/007_organize_algorithms_using_paramsets.md at main · querqy/chorus</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7a29508e8881" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My Segmentify Journey: From Intern to Part-Time Developer]]></title>
            <link>https://medium.com/segmentify-tech/my-segmentify-journey-from-one-month-intern-to-part-time-developer-219c72adfc99?source=rss-6cceeb5e0b89------2</link>
            <guid isPermaLink="false">https://medium.com/p/219c72adfc99</guid>
            <category><![CDATA[junior-developer]]></category>
            <category><![CDATA[internships]]></category>
            <category><![CDATA[women-in-tech]]></category>
            <category><![CDATA[segmentify]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Betul Ince]]></dc:creator>
            <pubDate>Sun, 19 Dec 2021 07:28:03 GMT</pubDate>
            <atom:updated>2022-01-04T12:03:21.876Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AP_ufPf3O3jor77fSmSWzA.png" /></figure><blockquote><em>“Without a transition, a change is just a rearrangement of the furniture. Unless transition happens, the change won’t work.” — </em><strong>William Bridges, Transitions: Making Sense of Life’s Changes</strong></blockquote><p>In this article, I will talk about what it feels like to do an internship with a company as a senior student and start working there as a part-time developer and step into the corporate world before finishing school. I will tell you how my experience has caused a paradigm shift in my world.</p><p>I consider my last six months of experience as a transition. All the essential information given in the school and all the technical words I heard during my education, such as agile environments, software development processes, meetings, teamwork, pairwork, know-hows, big projects, deployments, applications, million, billion, quadrillion lines of code, stress management, product management, problems related to self-confidence, etc. This experience has been highly enriching for me regarding technical and soft skills.</p><p>It is a great blessing to have the chance to be directly involved in the team’s daily tasks. For example, they could’ve easily given me tasks that had nothing to do with the team’s primary mission and said they were necessary to improve myself. And yes, technically, I would have improved in the end. However, I wouldn’t be able to get the company culture.</p><p>Prior to this internship at Segmentify, I wasn’t used to teamwork and always preferred to work alone. However, now I can clearly see that this experience has changed me for the better. Over time, constantly talking to others, asking questions, examining our shortcomings, and solving problems together led me to see how cool teamwork is!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*N3NwUXP-625YaAbmoQ6nUA.jpeg" /><figcaption>One fine day at the office</figcaption></figure><p>On the technical side, during my internship, I contributed to the research and analyses made during the development process by performing various tasks within the Search Team, which works to develop Segmentify’s Searchandising product. With the help of the tasks regarding this project and my mentor, I have gained a comprehensive understanding of Solr — an open-source, highly flexible search engine that is a part of the Apache Lucene project.</p><p>My Solr journey that started here encouraged me to contribute to this open-source Solr project after becoming a Junior Developer. As a result, the code I committed last week for UI development was merged! You never know what a 20-day internship experience will bring.</p><p>In addition to the assignments given to me during the internship, I also wrote daily reports to my mentor about what I did, learned, and researched. Each week I was given a new research topic: I’d do my research and write an essay with what I learned. This way, I gained extensive knowledge about DNS, git and GitHub, Continuous Integration, Jenkins, fuzzy logic, etc.</p><p>This is how my journey of blogging started. The benefits of regularly writing are innumerable to me. First and foremost, the knowledge ı’ve gained has flown away and will always be a record of it somewhere on the Internet. Secondly, I’m the one who wrote those articles that people were curious about or interested in!</p><p>Of course, there were times when I got stuck, couldn’t make progress, and lost my motivation, but now I realise that these times can be overcome by communication, asking for help, and doing the proper planning, not by pushing myself down even further.</p><p>As a result of my software engineering internship at Segmentify, I took part as an observer at every stage, from analysing the points that need to be developed in software to the time it was developed, tested, and deployed. As a result, I have gained experience figuring out the problem, the exact cause, how to solve it, the steps to be taken and how to plan while following this path. For example, I learned how to do indexing in a search engine by meeting with Solr, creating configset and collection that must be done before loading data. I also learned how to add a field-type, field, data, define a synonym, etc. Afterwards, I have performed an analysis by calculating how much time-saving the idea, which provides efficiency in terms of time during product feeding from Ignite, will save time if implemented. Additionally, I’ve observed and gained experience about the discipline and culture in business life on office days.</p><p>And that’s how my internship went. And when they asked me if I wanted to continue working together, of course, I wouldn’t have said no! So now I’ve been working at Segmentify for four months. I’m currently learning how to solve issues, improve, and examine the connections between Segmentify’s different products. My job also involves sending queries from Solr, learning about applications in the search team and preparing documents. In addition to that, I sometimes need to do something about Linux servers. Still, not only that, but I also get the opportunity to listen to what other teams are dealing with. I believe this is a very effective method to keep the connection and communication between teams alive.</p><p>And every day, even if I don’t have crucial and urgent tasks at hand, I feel that I am preparing myself for life as an engineer in the business and corporate spheres after school. I see this when my teammates talk about their daily tasks or when I attend their presentations, or when I spend time debugging codes just for the sake of trying, or when I prepare the documentation for an improvement that has been made after I test it.</p><p>I have seen that the knowledge gained at school comes to life in a company environment by fulfilling the given tasks and participating in projects. Therefore, even if there is a change, if there is no transition, the incoming information stays where it is, indicated by the quote in the introduction.</p><p>The most valuable thing I’ve learned on this journey at Segmentify is that <em>you should join the dance</em> if you want to learn.</p><p>Thank you for taking the time to read this post!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=219c72adfc99" width="1" height="1" alt=""><hr><p><a href="https://medium.com/segmentify-tech/my-segmentify-journey-from-one-month-intern-to-part-time-developer-219c72adfc99">My Segmentify Journey: From Intern to Part-Time Developer</a> was originally published in <a href="https://medium.com/segmentify-tech">Segmentify Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My Apache Solr Contribution Journey from UI Improvement to Bug Solving]]></title>
            <link>https://medium.com/segmentify-tech/my-apache-solr-contribution-journey-from-ui-improvement-to-bug-solving-d3380733deee?source=rss-6cceeb5e0b89------2</link>
            <guid isPermaLink="false">https://medium.com/p/d3380733deee</guid>
            <category><![CDATA[apache]]></category>
            <category><![CDATA[contribution]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[apache-solr]]></category>
            <category><![CDATA[solr]]></category>
            <dc:creator><![CDATA[Betul Ince]]></dc:creator>
            <pubDate>Thu, 16 Dec 2021 18:56:22 GMT</pubDate>
            <atom:updated>2026-02-11T23:27:11.140Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aq5xvC1InjCogGQrJt7Iqw.png" /></figure><blockquote><strong>“Contributing” to an Apache project is about more than just writing code — it’s about doing what you can to make the project better.</strong></blockquote><p><em>This article shows you how cool it is to contribute to open source by explaining the process until my code is merged in a detailed and straightforward way.</em></p><p>I improved Query Screen handling of raw query parameters. I changed the appearance of the “Raw Query Parameters” section in the Query Screen, added +/- buttons to enter query parameters one by one, and fixed one of the Solr 9.0.0 version’s bug, which is the query form was getting submitted two times each time you click the Execute Query button.</p><p><em>You can access the opened issue for this improvement from </em><a href="https://issues.apache.org/jira/browse/SOLR-15824"><em>JIRA</em></a><em>.</em></p><p><em>You can check out the pull request from </em><a href="https://github.com/apache/solr/pull/436"><em>GitHub</em></a><em>.</em></p><p>First of all, let me explain where this improvement idea came from. We are constantly using Solr at <a href="https://www.segmentify.com/">Segmentify</a>, where I am currently working as a junior developer in the search team. During the developments, to learn the source of the problem, we perform analyses by repeating the request sent to Solr from the website using the search product. When I say we repeat the request, I mean adding the values of the parameters in the outgoing request to fields of query screen such as q, fq, sort, fl, raw query parameters and then manipulating them, for example, adding, deleting or editing the values there.</p><p>But we had a problem while doing this; sometimes, we were entering very long queries in the raw query parameters, and due to the nature of this section, it was necessary to separate each value using either “&amp;” or “\n”.</p><pre>group.field=category_orig_TR&amp;group=true&amp;group.limit=16</pre><p>And, for example, when we wanted to change the first value, we had to go back one by one since this raw query parameters field is not expandable. So we couldn’t see all the parameters together as a whole.</p><p>On Query Screen, the raw query parameters section was looking like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/372/1*FGQ6Sile5Qtp8T-YG9bgXg.png" /></figure><p>As you can see, the “q” field is expandable, which allows us to see all the query parameters written in the q field together. Therefore, I first created an issue for improvement in <a href="https://issues.apache.org/jira/browse/SOLR-15824">JIRA</a>, thinking that if the raw query parameters field is made into an extensible structure like the q field, it will provide ease of use. I added a description like this:</p><p>“<em>The current Query Screen doesn’t have a structure that allows to enlarge the text box area and see all of the query parameters when long parameters are entered, for example, in the “raw query parameters” or “fq” field. In addition, if the line separating between the form and result regions in the div tag in index.html were resizable, it would be an easier way to use Query Screen.</em>”</p><p>Then I added raw query parameters inside <strong>textarea</strong> instead of <strong>input tag</strong> in the query.html file, and I set it vertical from query.css, committed it and opened a pull request with the issue number I opened.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c8f9b4b03e17024e76301cfbd7a120e6/href">https://medium.com/media/c8f9b4b03e17024e76301cfbd7a120e6/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d0895f30a101da6b4d02e39ced85a1d5/href">https://medium.com/media/d0895f30a101da6b4d02e39ced85a1d5/href</a></iframe><p>First, I did the same for the fq field, too, but after the following feedbacks, I decided that there was no need for such an improvement because there were +/- buttons in the fq field and the parameters were entered one by one. So I reverted the change. And the following comment came for the raw query parameters:</p><p><em>“I think it would be nice to have a + / — list for raw params like we have for fq … perhaps with a toggle between a list of textboxes or the textarea added to this PR? The textarea is an improvement for sure, but with a long list, it might still be better to separate each parameter in its own line.”</em></p><p>It made a lot of sense to me, and I added +/- buttons for the raw query parameters, similar to how it was done for fq, and I checked until the request was made and the result was received, and after I made sure that everything was working correctly, I pushed the code. The changes I made on query.html:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a87718f7646d98777b0d9318d76773db/href">https://medium.com/media/a87718f7646d98777b0d9318d76773db/href</a></iframe><p>Since the parameters to be entered in the raw parameters will be added to different boxes one after the other with the + button, not with “&amp;” side by side; I defined a variable named rawParams that takes each parameter and value-added to separate boxes in the raw parameters section and keeps it inside.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c294c3b5e204a25bb55945cfdca013b9/href">https://medium.com/media/c294c3b5e204a25bb55945cfdca013b9/href</a></iframe><p>Each rawP represents the value inside the raw query parameters box.</p><p>The insertToRawParams function takes two variables named argKey and argValue. ArgKey is the parameter we entered in raw parameters; argValue takes the value of this parameter. I changed the insertToRawParams function, as you can see below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f41b14841f7589916b56b7bd7dc1a2be/href">https://medium.com/media/f41b14841f7589916b56b7bd7dc1a2be/href</a></iframe><p>So we can check the raw query parameters boxes, take the value in each of them, and keep them in the rawParams that we have created.</p><p>I edited <em>the addFilters</em> function as follows so that it can handle the parameters entered in the raw query:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9bb460345281d366900d6d71bc5ee454/href">https://medium.com/media/9bb460345281d366900d6d71bc5ee454/href</a></iframe><p>In addFilters, in the code snippet above, the boxes that meet this rawP value are checked one by one to see if the parameters have been entered side by side, then they are split, and the value is assigned. Because there were no +/- buttons before, the parameters were being entered side by side or one under the other with “&amp;”. Although there are +/- buttons in this new feature, we still want the ability to enter more than one parameter with their values in a single box.</p><p>And finally, of course, I added functions for +/- buttons to create a new raw parameters box when clicking the “+” button and delete it by clicking “-”:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f78ff0b704265bfe900bab8e525bd64d/href">https://medium.com/media/f78ff0b704265bfe900bab8e525bd64d/href</a></iframe><p><em>Voilà! I sent my second commit.</em></p><p>But, I missed a point in the commit I sent for the previous raw parameters improvement. The problem was; with our new improvement about the raw query parameters field, if we add more than one parameter with their values ​​one after the other with the + button and we want to make more than one request with the same parameters, the page would not be refreshed because the URL would not change (because all query parameters were kept in urlParams and added to the URL).</p><p>This is due to the working logic of $location.search(). This function only refreshes the page if there is a change in the URL. Therefore, if we execute it with the same parameter a second time, it will be renewed once.</p><p>Another problem was that if you add the same parameter more than once, it goes to the <em>server</em> in a comma-separated form. At the same time, since the page was refreshed in the query form, the values ​​in different boxes were sent to the raw query parameters in a single box, separated by commas:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rA6vcLhmwJAIUK3baYjT_A.png" /></figure><blockquote>Suppose you supply the same parameter more than once, such as stats.field (to get stats for multiple fields), then the values get concatenated into a single comma-delimited list sent to the server like &quot;stats.field&quot;:&quot;pages_i,price&quot;.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hi4R26eSotIEOoby-9eFgw.png" /></figure><p>Because of what I mentioned above, as seen in the screenshot, after the first successful query was sent, when the query parameters that would give an error were entered, and the execute query button was clicked, the query form was sent twice.</p><p><strong>Now let’s observe the commits I sent to solve all these problems I mentioned, and let’s get one step closer to merging the code.</strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1a2a4bc00e11e538c0bc5b35867c373e/href">https://medium.com/media/1a2a4bc00e11e538c0bc5b35867c373e/href</a></iframe><p>I defined the $route parameter to refresh the page manually later.</p><p>I changed the insertToRawParams function <em>again:</em></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/199b68baf0492cae6a1ef68bffb1b00c/href">https://medium.com/media/199b68baf0492cae6a1ef68bffb1b00c/href</a></iframe><p>It is checked whether the type of the argValue value is Array because the problem was that when we wanted to use the same parameter more than once, it went to the server separated by commas.</p><p>$location.search() runs every time query.js runs; it adds the parameters given in the query form to the URL.</p><p>The solution is as follows: As I explained above, $location.search only reloads the page when there is a change in the URL, so when Execute Query is called, it will no longer run the query and print the result. First, it will add the parameters to the URL, then refresh the page, and after the page is refreshed, the doQuery will run. Therefore, it will run the query and print the result at that stage. I provided this with the <strong>isPageReload</strong> control.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d746f4efa4ebd96183960238e2292672/href">https://medium.com/media/d746f4efa4ebd96183960238e2292672/href</a></iframe><p>In the following if-condition, it is checking if the page is reloaded. isPageReload goes into doQuery as true if the page is reloaded and undefined if it isn’t. In the if(isPageReload) block inside the doQuery function, the action is taken according to the value of this variable:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/759cea8891f18dfac5a59653b22f8ef4/href">https://medium.com/media/759cea8891f18dfac5a59653b22f8ef4/href</a></iframe><p>The Execute Query button executes doQuery. The code goes inside the doQuery function, so I edited the doQuery function as follows so that it can handle the parameters entered in the raw query.</p><p>I created a control block with <em>if (isPageReload) { </em>inside the doQuery function:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a79cad96b76e652bf38c3479e0d36a2e/href">https://medium.com/media/a79cad96b76e652bf38c3479e0d36a2e/href</a></iframe><p>Here, thanks to the <em>for( var key in adminParams ){</em> loop, the values of all parameters are added to adminParams one by one. Although a field is used twice since it is an Array type, no errors occur, and values do not get concatenated into a single comma-delimited list.</p><p><strong>Now it goes to the URL correctly even if the same parameter is used multiple times.</strong></p><p>When isPageReload is <em>undefined</em> in the above if block, that is, the page is not refreshed, the previous URL and the current URL are found, and it is checked whether these two URLs are equal to each other because when the URL is the same, the page is not reloaded. By controlling this, we try to reload the page manually. If it is the same, we reload it to run the query after the page is refreshed and the response is displayed on the screen.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/813/1*fvo52JC4jctV96vToASMNQ.png" /></figure><p>As seen in the screenshot, even if the same parameter is given different values in different raw parameters boxes and called Execute Query, it is sent to the server correctly.</p><p>And the request is not sent twice, only once:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/821/1*MxiA5IZgFeoq7O_K0Dkplw.png" /></figure><p>And after this improvement, the raw query parameters field looks like this without any errors:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/381/1*uk5JK50tQ-e7HeYuA1XC5Q.png" /></figure><p>Now that everything is solved, you can enter your raw query parameters one by one with the +/- buttons and enjoy your simplified query screen!</p><p>Lastly, I would like to thank my mentor <a href="https://www.linkedin.com/in/ahmetcankepenek/?originalSubdomain=tr">Ahmet Can Kepenek</a>, for his unwavering support and helpful feedbacks, and Solr developers for encouraging me and giving me cool suggestions. 🚀</p><p>Thank you for reading!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d3380733deee" width="1" height="1" alt=""><hr><p><a href="https://medium.com/segmentify-tech/my-apache-solr-contribution-journey-from-ui-improvement-to-bug-solving-d3380733deee">My Apache Solr Contribution Journey from UI Improvement to Bug Solving</a> was originally published in <a href="https://medium.com/segmentify-tech">Segmentify Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Some Linux Commands That Will Help You A Lot — 1]]></title>
            <link>https://medium.com/@Betulince/some-linux-commands-that-will-help-you-a-lot-1-c95ab182e377?source=rss-6cceeb5e0b89------2</link>
            <guid isPermaLink="false">https://medium.com/p/c95ab182e377</guid>
            <dc:creator><![CDATA[Betul Ince]]></dc:creator>
            <pubDate>Wed, 10 Nov 2021 16:09:42 GMT</pubDate>
            <atom:updated>2026-02-09T12:35:25.496Z</atom:updated>
            <content:encoded><![CDATA[<h3>Some Linux commands that will help you a lot</h3><h4>In A Nutshell</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*ZbZ-x0GaGo5ohKDKUPdc6A.jpeg" /></figure><p>As you know, Linux is a command line based operating system. If you know a few basic commands, you can save too much time while working on your projects. In this post, I will try to show you some lifesaver commands that can help you a lot during your journey to become a Linux master.</p><h3>1. Use grep if you need to search for a particular pattern</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qOEEz_TyqNw_CpRk4Gc5fA.png" /></figure><blockquote><strong>g/re/p </strong>stands for; <strong>g</strong>lobally search for a <strong>re</strong>gular expression and <strong>p</strong>rint matching lines.</blockquote><p>With grep command you can search text and strings that you want in a given file. Syntax is as follows:</p><pre>grep [options] patterns [file]</pre><p>options : command qualifiers</p><p>patterns : required search query</p><p><strong>Some useful options:</strong></p><ul><li>-i : The search will not be case sensitive. So if you want to search for the word “computer” then “COMPUTER” will also give the same result.</li><li>-n: Showing matching line numbers.</li></ul><p>In the example below, at first we didn’t give -i option and grep couldn’t find the pattern “The”. Because we were searching for “the” with lower case “t”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/846/1*qyAFmooSmRSiNi9F6-OPHA.png" /></figure><ul><li>-r : Enables recursive search in current directory.</li><li>-c: Counting the number of matches.</li><li>-v : Displays the rows that don’t match the searched pattern.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/338/1*ZUg4uFJQ7gCPwiax_cymjw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/368/1*Rduf6hRl55B4n8NZWTX1eQ.png" /></figure><p>If you combine grep commands, you can find patterns hiding in thousands of files. For example you can pipe the output of the “cat” command to grep for a particular pattern:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/821/1*YWdmDbRL66-PW40NptMt-w.png" /></figure><h3>2. What about awk?</h3><p>awk syntax is as follows:</p><pre>awk &#39;program&#39; [file]</pre><p>Let’s say that you have a list of data sorted one after the other and you want to print them side by side separated by comma, then you can use awk:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nDQ24mPyJlO-osJrlL-l-A.png" /></figure><p>awk ORS is a built-in variable. With the delimiter you give to ORS, each record in the output will be printed with this delimiter.</p><p><strong>Built-in Variables:</strong></p><ul><li>NF - gives you the total number of fields in a record.</li><li>NR- gives you the total number of records being processed or line number.</li><li>FILENAME - gives the name of the file being read.</li><li>FS - Field separator.</li><li>RS- Record separator.</li><li>OFS - Output field separator.</li><li>- Output record separator.</li></ul><p>awk NR example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/959/1*RJbH5Xn-G4YlJhiOLokn4g.png" /></figure><p>As you can see NR gave the total number of records being processed.</p><p>With awk, you can control to process data. You can define variables, use string and arithmetic operations, use control flow and loops or generate formatted reports. You can process files with millions of lines to output a readable report that you can benefit from.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/409/1*8zo6C_CVFEeUGB7G6NjUUQ.png" /></figure><p>As you can see in the example above maybe we don’t need all the information that will come from a command and we just want to see the first field. We can pipe the output from who into awk and then tell awk to print only the first field.</p><ul><li>$0: Represents the entire line of text.</li><li>$1: Represents the first field.</li><li>$5: Represents the fifth field.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/689/1*ubw4mzI7ycIyyxoPp4NlCQ.png" /></figure><p>A BEGIN rule used to perform actions before records are processed. Actually, it’s executed before even reads any text. And END rule performs actions after records are processed. You can have multiple BEGIN and END rules, and they’ll execute in order. For example, maybe you want to print the sum of the numbers in a field:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/568/1*P16QhtA2IxSpmz8NxUq9Vg.png" /></figure><p>This command calculates the total and print the sum in the end of the execution.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/745/1*F-ERmWyy2ki2uS_hE2NNBg.png" /></figure><p>To print 10 random numbers from 0 to 9, you can use awk command. As you can see, the program statement tells awk what operation to do.</p><h3>3. You can do too many things with sed</h3><blockquote>sed stands for <strong>s</strong>tream <strong>ed</strong>itor for filtering and transforming text</blockquote><p>sed is used for text substitution, find and replace operations, text manipulations like search, insertions deletion. And the good thing is with sed, we can perform all these operations on a file without opening it.</p><p>Syntax is as follows</p><pre>sed [options] {script} [file]</pre><p>— We can replace or substitute the string in a file. The example below shows simple sed command replaces the word “Unix” with “Linux” in the file.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/756/1*oKjw1Zm-VHIY1xco0iLDZw.png" /></figure><p>— We can do the same thing for a particular occurence of a pattern in a <strong>line</strong>. So if we use /1, /2, /4 etc. flags, we can replace the first, second or fourth occurence of a pattern in a line. Let’s change the third occurence of a word “Unix” with “Linux” in a line:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/423/1*X-bfW5K4t29aft4wLmt3Dg.png" /></figure><p>— /g flag (stands for global replacement) replace all the occurences of the string in a <strong>line</strong>.</p><pre>$sed &#39;s/Unix/Linux/g&#39; text.txt</pre><p>— /1, /2, /4 etc + /g replace all the patterns <strong>from</strong> nth occurence of a pattern in a line.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/431/1*zrusuRs8C9iGwHSiX-w2Ag.png" /></figure><p>— We can delete particular pattern:</p><pre>sed &#39;/pattern/d&#39; text.txt</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/340/1*zBa0kjU09iStDfPsY2PxTg.png" /></figure><p>— We can delete lines, delete line from range x to y:</p><pre>sed &#39;nd&#39; text.txt<br>sed &#39;x,yd&#39; text.txt</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/331/1*tnDdNECRFmwdBDwgKXZfdQ.png" /></figure><p>— We can replace string on a specific line number or replace string on a range of files. For example if we have a file like this:</p><pre>[Unix]<br>[Unix]<br>[Unix]<br>[Unix]<br>[Unix]</pre><p>And if we want to replace the “[“ with space, between the first and third line or only the third line:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/390/1*pp7OEa82gD0ieOVfCgvaMg.png" /></figure><p>Or we can combine cut and sed commands to delete ‘[ ]’ next to pattern:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/539/1*8E0eVotn-TOOrsGXfu3fyA.png" /></figure><p>Also sed can act like grep by combining the print operator(p) to function on all lines that match a particular pattern:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/385/1*UTnd6-dfUstPqTZO55E3ng.png" /></figure><h3><strong>Conclusion</strong></h3><p>The above commands and examples are just the tip of the iceberg. For someone who knows how to use them effectively, these commands can really come in handy. We can combine them in different ways to get what we need.</p><p>Thank you for reading!</p><h3>References</h3><ul><li><a href="https://www.cyberciti.biz/faq/howto-use-grep-command-in-linux-unix/">How To Use grep Command In Linux / UNIX With Practical Examples</a></li><li><a href="https://www.guru99.com/linux-pipe-grep.html">Pipe, Grep and Sort Command in Linux/Unix with Examples</a></li><li><a href="https://www.grymoire.com/Unix/Sed.html">The Grymoire&#39;s tutorial on SED</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c95ab182e377" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Synonyms in Solr]]></title>
            <link>https://medium.com/@Betulince/synonyms-in-solr-3097d8023ec2?source=rss-6cceeb5e0b89------2</link>
            <guid isPermaLink="false">https://medium.com/p/3097d8023ec2</guid>
            <category><![CDATA[filters]]></category>
            <category><![CDATA[synonyms]]></category>
            <category><![CDATA[fuzzy]]></category>
            <category><![CDATA[solr]]></category>
            <category><![CDATA[search]]></category>
            <dc:creator><![CDATA[Betul Ince]]></dc:creator>
            <pubDate>Wed, 18 Aug 2021 11:21:57 GMT</pubDate>
            <atom:updated>2021-08-18T11:21:57.262Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*q32PXOPaNgVmFOlE0TUIig.png" /></figure><p>Last week I was in trouble with Solr’s synonyms. I was trying to understand what synonym means in Solr, how to define one for some collection, how to test whether this synonym I defined works or not and as if that wasn’t enough I was looking for a way to see the relevant data even though I made a typo while I was searching for the synonym I defined for a word in my data.</p><blockquote>With this article, we will explore the use of synonyms and fuzzy together. But first let’s go through some basics and give three examples so you can fully understand.</blockquote><h4>Why we use synonyms in search ?</h4><p>Imagine you speak Turkish as your mother tongue and you’re searching for skirts on your favorite shopping site which is locally in New York. You’re going into search box, typing “etek” which is the equivalent of the word skirt in English and expecting to see all “skirt” relevant data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/1*L0OIAg1bEL5v97VQF1Wt0w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/1*PRlSZ0oNO00LhmHIbZfiWQ.png" /><figcaption>We could not find skirts by searching with the term “etek”</figcaption></figure><p>Synonyms are used to fix some relevancy problems. As you can see in the picture above, you searched for “etek” but the site replied with some irrelevant products.</p><p>Defining synonyms allows you to extend some terms based on their equivalence. Thus, users can reach the desired result when they search, even with a word that might not have been used in the original data.</p><p>To give an example, it allows users to find computers by searching for “Computer” and also by searching for “PC”, although the original data contains only “Computer”. This means PC and Computer are two different terms with the same meaning. So user will find all the products containing either PC or Computer by using any of those terms in the query.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rHEbJscBc9pUmzc64x9JCw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WlNjTnxj7N8SGg23AYAtfw.png" /><figcaption>Synonyms do not work here.</figcaption></figure><p>We need to have the synonyms file which is a part of the Solr configuration in order to apply our needs. This file contains all the synonyms for the data in it. As I mentioned above, more than one synonym can be defined, not just one. For example; data might contain the words, “Blue Jean, Skinny Jeans, Slim-Fit Jeans, Regular Jeans, Cigarette Jean, Mom Jeans” for the term Blue Jean. When someone searches, they search for only one of these terms, but expects all these results to come up. To meet this expectation, it makes sense to create a file to contain similar terms specific to my data. You can also use language mixes that will be useful in some contexts, as in the language example I gave above.</p><blockquote>Now i will show you how you can define it assuming you understand synonyms and know a little bit about Solr.</blockquote><h4>Define Synonyms on Solr</h4><p>In order to define and use synonyms, it is necessary to go through several stages. I will explain all of them one by one in the following parts. I should also give the detail that I am working on Solr Cloud.</p><ul><li><a href="https://medium.com/@Betulince/introduction-to-apache-solr-what-is-solr-how-to-install-on-ubuntu-20-04-3581f5c695ef">In my previous post</a>, I gave a brief introduction about collections, field-types and fields. To define a synonym and search for it, as you can imagine, you first need to create a configset and a collection. Then you need to define a filter that states that you will define synonym in this collection. You need to define this filter:</li></ul><pre>{<br>&quot;class&quot;:&quot;org.apache.solr.rest.schema.analysis.ManagedSynonymGraphFilterFactory$SynonymManager&quot;<br>}</pre><p>I did this by sending a request to my collection with Python like this:</p><pre>import requests<br>import json<br><br>url = &quot;http://localhost:7574/solr/my_collection_name/schema/analysis/synonyms/en&quot;<br><br><strong><em>payload = json.dumps({<br>  &quot;class&quot;: &quot;org.apache.solr.rest.schema.analysis.ManagedSynonymGraphFilterFactory$SynonymManager&quot;<br>})</em></strong></pre><pre>headers = {&#39;Content-Type&#39;: &#39;application/json&#39;}<br>response = requests.request(&quot;POST&quot;, url, headers=headers, data=payload)<br>print(response.text)</pre><p>At the end of the URL, you can see “en” which indicates that I will use the English synonym definition.</p><ul><li>Then you need to send request specifying what will be the synonym in this collection’s data. I defined a Turkish synonym for “midi skirt” as “kisa etek”:</li></ul><pre>import requests<br>import json<br><br>url = &quot;http://localhost:7574/solr/my_collection_name/schema/analysis/synonyms/en&quot;</pre><pre><strong><em>payload = json.dumps({<br>    &quot;Midi Skirt&quot;: [<br>        &quot;Kisa Etek&quot;<br>    ]<br>})</em></strong></pre><pre>headers = {&#39;Content-type&#39;: &#39;application/json&#39;}<br>response = requests.request(&quot;PUT&quot;, url, headers=headers, data=payload)<br>print(response.text)</pre><p>After this a file with the name of “_schema_analysis_synonyms_en.json” will be created in the Solr panel’s files and its content will contains this:</p><pre>&quot;<strong>managedMap</strong>&quot;:{     <br>&quot;<strong>Midi Skirt</strong>&quot;:[&quot;kisa etek&quot;],     <br>&quot;<strong>class:</strong>[&quot;org.apache.solr.rest.schema.analysis.ManagedSynonymGraphFilterFactory$SynonymManager&quot;]}</pre><ul><li>It’s time to prepare the place where data will come, I mean adding field-types and fields. But now I would like to take a break from explaining how to define synonyms and talk about the thing that I was working on for two weeks.</li></ul><p><em>During my internship</em>, I realized that in the company, they are defining filters on the query analyzer while adding field-types. There is a reason for this. You can define synonym graph filter on the index time or on the query time. The difference is important because it’s changing everything.</p><p>My mentor asked from me to find a solution for some problem. When I defined a synonym for a word in my data and searched for that synonym, I was able to get all the relevant results. But if this synonym word that I defined doesn’t appear anywhere in my data and I made a typo during the search phase, I couldn’t reach the results I wanted. For example, if I defined the synonym of “kisa etek” for “midi skirt” and searched for “kisa etek”, I could find all “midi skirt”s, but when I made a typo in the form of “kisd etl”, I was getting very irrelevant results even though I defined fuzzy which allows us to make typos and gives us the right to make mistakes according to the fuzzy value we give. Of course, this happens when you define the filter in the query analysis.</p><blockquote>While researching, I learned that If I define the filter in index analysis, this problem disappears and I can reach the results I want even if I make a typo.</blockquote><p>In the next steps, I will continue as I defined the synonym in the index analysis. But you should know that this creates new problems:</p><p>— The index might get bigger, because all synonyms must be indexed.</p><p>— Changes in the synonym rules require reindexing of documents and reindexing is a very time consuming, inefficient way. I mean synonym rules can’t be changed for existing documents without reindexing.</p><p>But for now I don’t care about it because my main goal was to use fuzzy search and synonym together. Now let’s continue:</p><ul><li>In schema.xml add a field-type that indicates the synonym for the field. You need to use the synonym filter and indicate the language which is managed=”en”, and besides that it’s required to add FlattenGraphFilterFactory filter on index analyzers after graph filters.</li></ul><pre>{<br>  &quot;name&quot;: &quot;sgm_text_en&quot;,<br>  &quot;class&quot;: &quot;solr.TextField&quot;,<br>  &quot;multiValued&quot;: false,<br>  &quot;indexAnalyzer&quot;: {<br>    &quot;filters&quot;: [<br><strong>     <em> {<br>        &quot;class&quot;: &quot;solr.ManagedSynonymGraphFilterFactory&quot;,<br>        &quot;managed&quot;: &quot;en&quot;<br>      },<br>      {<br>        &quot;class&quot;: &quot;solr.FlattenGraphFilterFactory&quot;<br>      },</em></strong><br>      {<br>        &quot;class&quot;: &quot;solr.TrimFilterFactory&quot;<br>      },<br>      {<br>        &quot;class&quot;: &quot;solr.LowerCaseFilterFactory&quot;<br>      },<br><br>      {<br>        &quot;class&quot;: &quot;solr.ASCIIFoldingFilterFactory&quot;,<br>        &quot;preserveOriginal&quot;: &quot;true&quot;<br>      },<br>      {<br>        &quot;class&quot;: &quot;solr.RemoveDuplicatesTokenFilterFactory&quot;<br>      }<br>    ],<br>    &quot;tokenizer&quot;: {<br>      &quot;class&quot;: &quot;solr.StandardTokenizerFactory&quot;<br>    }<br>  },<br>  &quot;queryAnalyzer&quot;: {<br>    &quot;filters&quot;: [<br>      {<br>        &quot;class&quot;: &quot;solr.TrimFilterFactory&quot;<br>      },<br>      {<br>        &quot;class&quot;: &quot;solr.LowerCaseFilterFactory&quot;<br>      },<br>      {<br>        &quot;class&quot;: &quot;solr.ASCIIFoldingFilterFactory&quot;,<br>        &quot;preserveOriginal&quot;: &quot;true&quot;<br>      },<br>      {<br>        &quot;class&quot;: &quot;solr.RemoveDuplicatesTokenFilterFactory&quot;<br>      }<br>    ],<br>    &quot;tokenizer&quot;: {<br>      &quot;class&quot;: &quot;solr.StandardTokenizerFactory&quot;<br>    }<br>  }<br>}</pre><ul><li>Once the filed type is added, we can add field that maps to the field type by using the name.</li></ul><pre>{<br>  &quot;name&quot;: &quot;name_en&quot;,<br>  &quot;type&quot;: &quot;sgm_text_en&quot;,<br>  &quot;stored&quot;: &quot;true&quot;,<br>  &quot;indexed&quot;: &quot;true&quot;,<br>  &quot;indexType&quot;: &quot;field&quot;,<br>  &quot;multiValued&quot;: &quot;false&quot;,<br>},</pre><ul><li>And after adding our documents, we can search for “kisa etek” and get the results with full of “midi skirt”s:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TdUJfW9Z9QeYLj9WkbcFNg.png" /></figure><p>And here even if I made a typo while searching, i got all the relevant data:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HyBroDU_Dfew96VOOSslfQ.png" /></figure><p>Thank you for reading!</p><h4>Reference</h4><ul><li><a href="https://www.drupal.org/project/search_api_solr/issues/2997341">Multi-term synonyms plus fuzzy search</a></li><li><a href="https://solr.apache.org/guide/8_6/managed-resources.html">Managed Resources</a></li><li><a href="https://library.brown.edu/create/digitaltechnologies/using-synonyms-in-solr/">Using synonyms in Solr</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3097d8023ec2" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>